<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>林德熙</title>
    <description>微软最具价值专家和 .NET 基金会成员</description>
    <link>/</link>
    <atom:link href="/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Tue, 07 Apr 2026 23:04:28 +0000</pubDate>
    <lastBuildDate>Tue, 07 Apr 2026 23:04:28 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>博客导航</title>
        <description>&lt;p&gt;本文记录我写的博客和我收集的博客&lt;/p&gt;

&lt;!--more--&gt;

&lt;!-- CreateTime:2022/6/28 19:49:23 --&gt;
&lt;!-- 置顶 --&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;h3 id=&quot;dotnet-职业技术学院&quot;&gt;dotnet 职业技术学院&lt;/h3&gt;

&lt;p&gt;Skype 群： &lt;a href=&quot;https://join.skype.com/TiYcws3RHXOO&quot;&gt;https://join.skype.com/TiYcws3RHXOO&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;电报群： &lt;a href=&quot;https://t.me/dotnet_campus&quot;&gt;https://t.me/dotnet_campus&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这是公开可加入的群&lt;/p&gt;

&lt;h3 id=&quot;cpf-xamarin-maui-uno-国产-uos-开发&quot;&gt;CPF xamarin MAUI UNO 国产 UOS 开发&lt;/h3&gt;

&lt;p&gt;群: 810052083&lt;/p&gt;

&lt;p&gt;交流国产系统开发相关，以及 MAUI 和 Avalonia 和 UNO 和 xamarin 和 Mono 在这些神奇的系统上运行的问题，以及这些神奇的系统的系统环境问题&lt;/p&gt;

&lt;p&gt;也可以聊聊 GTK 和 X11 系列的问题&lt;/p&gt;

&lt;h3 id=&quot;microsoftmauigraphics&quot;&gt;Microsoft.Maui.Graphics&lt;/h3&gt;

&lt;p&gt;群：623209342&lt;/p&gt;

&lt;p&gt;交流使用 MAUI 的 Graphics 进行渲染的技术，包括自绘平台以及图形绘制相关技术&lt;/p&gt;

&lt;h3 id=&quot;wpf-sharpdx-vortice&quot;&gt;WPF SharpDx Vortice&lt;/h3&gt;

&lt;p&gt;群：622808968&lt;/p&gt;

&lt;p&gt;交流使用 SharpDx 或 Vortice 调用 DirectX 底层的技术群，包括 D2D 和图形绘制相关技术&lt;/p&gt;

&lt;h3 id=&quot;dotnet-uno&quot;&gt;dotnet UNO&lt;/h3&gt;

&lt;p&gt;群：724181515&lt;/p&gt;

&lt;p&gt;专门用来交流 &lt;a href=&quot;https://github.com/unoplatform/uno&quot;&gt;UnoPlatform/Uno&lt;/a&gt; 开发技术的群&lt;/p&gt;

&lt;h3 id=&quot;skiasharp&quot;&gt;SkiaSharp&lt;/h3&gt;

&lt;p&gt;群: 788018852&lt;/p&gt;

&lt;p&gt;交流 SkiaSharp 相关技术&lt;/p&gt;

&lt;h3 id=&quot;opentk&quot;&gt;OpenTK&lt;/h3&gt;

&lt;p&gt;群：789025426&lt;/p&gt;

&lt;p&gt;交流使用 OpenTK 调用渲染层的群&lt;/p&gt;

&lt;h3 id=&quot;openxml-office-开发&quot;&gt;OpenXML Office 开发&lt;/h3&gt;

&lt;p&gt;群：688921958&lt;/p&gt;

&lt;p&gt;交流 Office 开发和 OpenXML 相关技术的群，此群非新手群&lt;/p&gt;

&lt;h3 id=&quot;windows-定制&quot;&gt;Windows 定制&lt;/h3&gt;

&lt;p&gt;群：729474199&lt;/p&gt;

&lt;p&gt;聊 OEM 定制开发，以及各种奇奇怪怪的 Win32 API 的使用方法。非 Windows 使用方法群，非编程新手友好群&lt;/p&gt;

&lt;p&gt;也可以吐槽微软的系统的各种问题&lt;/p&gt;

&lt;h3 id=&quot;roslyn&quot;&gt;Roslyn&lt;/h3&gt;

&lt;p&gt;群：629370288&lt;/p&gt;

&lt;p&gt;聊聊 Roslyn 和编译定制，预编译，定制构建过程的群&lt;/p&gt;

&lt;h3 id=&quot;semantickernel&quot;&gt;SemanticKernel&lt;/h3&gt;

&lt;p&gt;群: 623349574&lt;/p&gt;

&lt;p&gt;聊聊 AI 应用&lt;/p&gt;

&lt;h3 id=&quot;uwp-wpf-开发交流群&quot;&gt;UWP WPF 开发交流群&lt;/h3&gt;

&lt;p&gt;群：XXXXXXXXXX&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;/&quot;&gt;/&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;使用 GitHub Page 搭建的博客&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;博客园： &lt;a href=&quot;https://www.cnblogs.com/lindexi&quot;&gt;https://www.cnblogs.com/lindexi&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;作为主站的备份&lt;/li&gt;
      &lt;li&gt;靠自动同步更新&lt;/li&gt;
      &lt;li&gt;图片有些是 http 的，可能会看不到图片&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;皮卡帮忙搭建的博客（国内）：&lt;a href=&quot;https://lindexi-blog.jgrass.cc/&quot;&gt;https://lindexi-blog.jgrass.cc/&lt;/a&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;/li&gt;
  &lt;li&gt;皮卡帮忙搭建的博客（国外）：&lt;a href=&quot;https://lindexi-blog.vercel.app/&quot;&gt;https://lindexi-blog.vercel.app/&lt;/a&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;GitBook ： &lt;a href=&quot;https://uwp.gitbook.io/&quot;&gt;https://uwp.gitbook.io/&lt;/a&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;/li&gt;
  &lt;li&gt;腾讯云：&lt;a href=&quot;https://cloud.tencent.com/developer/column/71201&quot;&gt;https://cloud.tencent.com/developer/column/71201&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;靠自动同步更新&lt;/li&gt;
      &lt;li&gt;同步过来的图片自动转 https 链接，有些图片会丢&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;CSDN ：&lt;a href=&quot;https://blog.csdn.net/lindexi_gd/&quot;&gt;https://blog.csdn.net/lindexi_gd/&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;不推荐，大量博客都需要 vip 才能看，博客也很久同步不上去了&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;51CTO：&lt;a href=&quot;https://blog.51cto.com/u_11283245&quot;&gt;https://blog.51cto.com/u_11283245&lt;/a&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;/li&gt;
  &lt;li&gt;medium：&lt;a href=&quot;https://medium.com/@lindexi_gd&quot;&gt;https://medium.com/@lindexi_gd&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;基本只发英文博客&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;!-- 可在 CayqeargugarYulijohem 更新博客导航到博客园 --&gt;

&lt;h2 id=&quot;语言&quot;&gt;语言&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E9%AB%98%E7%BA%A7%E9%9D%A2%E8%AF%95%E9%A2%98.html&quot;&gt;C# 高级面试题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%B8%83%E5%B0%94%E5%8F%AF%E4%BB%A5%E6%98%AF-true-%E6%88%96-false-%E5%A4%96%E7%9A%84%E5%80%BC.html&quot;&gt;dotnet C# 布尔可以是 true 或 false 外的值&lt;/a&gt;
&lt;!-- [dotnet C# 布尔可以是 true 或 false 外的值 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18930537 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%9F%BA%E7%A1%80-%E4%B8%BA%E4%BB%80%E4%B9%88-GetHashCode-%E6%8E%A8%E8%8D%90%E5%8F%AA%E5%8F%96%E5%8F%AA%E8%AF%BB%E5%B1%9E%E6%80%A7%E6%88%96%E5%AD%97%E6%AE%B5%E5%81%9A%E5%93%88%E5%B8%8C%E5%80%BC.html&quot;&gt;dotnet C# 基础 为什么 GetHashCode 推荐只取只读属性或字段做哈希值&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%88%86%E4%BA%AB%E5%9F%BA%E7%A1%80-for-%E5%BE%AA%E7%8E%AF%E7%9A%84%E5%86%99%E6%B3%95.html&quot;&gt;dotnet C# 分享基础 for 循环的写法&lt;/a&gt;
&lt;!-- [dotnet C# 分享基础 for 循环的写法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18351898 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%A6%82%E4%BD%95%E5%9C%A8%E9%A1%B6%E7%BA%A7%E8%AF%AD%E5%8F%A5%E5%AE%9A%E4%B9%89%E5%B1%9E%E6%80%A7.html&quot;&gt;dotnet C# 如何在顶级语句定义属性&lt;/a&gt;
&lt;!-- [dotnet C# 如何在顶级语句定义属性 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18387839 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E4%BD%BF%E7%94%A8-using-%E5%85%B3%E9%94%AE%E5%AD%97%E9%97%B4%E6%8E%A5%E5%AE%9E%E7%8E%B0%E5%8F%AA%E8%AF%BB%E5%B1%80%E9%83%A8%E5%8F%98%E9%87%8F%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;C# 使用 using 关键字间接实现只读局部变量的方法&lt;/a&gt;
&lt;!-- [C# 使用 using 关键字间接实现只读局部变量的方法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19146902 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E9%87%8C%E9%9D%A2%E5%BE%88%E5%B0%91%E4%BA%BA%E7%9F%A5%E9%81%93%E4%BD%86%E5%BE%88%E5%A5%BD%E7%94%A8%E7%9A%84-Tuple-%E8%BD%AC%E6%8D%A2.html&quot;&gt;C# 里面很少人知道但很好用的 Tuple 转换&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-await-%E9%AB%98%E7%BA%A7%E7%94%A8%E6%B3%95.html&quot;&gt;C＃ await 高级用法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%A4%9A%E6%AC%A1%E5%AF%B9%E4%B8%80%E4%B8%AA%E5%AF%B9%E8%B1%A1%E8%B0%83%E7%94%A8%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E4%BC%9A%E5%8F%91%E7%94%9F%E4%BB%80%E4%B9%88.html&quot;&gt;dotnet C# 多次对一个对象调用构造函数会发生什么&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%8F%AA%E5%88%9B%E5%BB%BA%E5%AF%B9%E8%B1%A1%E4%B8%8D%E8%B0%83%E7%94%A8%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet C# 只创建对象不调用构造函数方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E4%B8%BB%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E5%B8%A6%E6%9D%A5%E7%9A%84%E8%99%9A%E5%B1%9E%E6%80%A7%E4%BC%98%E5%8A%BF.html&quot;&gt;dotnet C# 主构造函数带来的虚属性优势&lt;/a&gt;
&lt;!-- [dotnet C# 主构造函数带来的虚属性优势 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19300095 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%A6%82%E6%9E%9C%E5%9C%A8%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E6%8A%9B%E5%87%BA%E5%BC%82%E5%B8%B8-%E6%9E%90%E6%9E%84%E5%87%BD%E6%95%B0%E6%98%AF%E5%90%A6%E4%BC%9A%E6%89%A7%E8%A1%8C.html&quot;&gt;dotnet C# 如果在构造函数抛出异常 析构函数是否会执行&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%9C%A8-finally-%E6%8A%9B%E5%BC%82%E5%B8%B8%E4%BC%9A%E5%8F%91%E7%94%9F%E4%BB%80%E4%B9%88.html&quot;&gt;dotnet C# 在 finally 抛异常会发生什么&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%A6%82%E6%9E%9C%E5%9C%A8%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E6%8A%9B%E5%87%BA%E5%BC%82%E5%B8%B8-%E6%98%AF%E5%90%A6%E5%8F%AF%E4%BB%A5%E6%8B%BF%E5%88%B0%E5%AF%B9%E8%B1%A1%E8%B5%8B%E5%80%BC%E7%9A%84%E5%8F%98%E9%87%8F.html&quot;&gt;dotnet C# 如果在构造函数抛出异常 是否可以拿到对象赋值的变量&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%B8%BA%E4%BD%95-C-%E7%9A%84%E6%8C%87%E9%92%88%E4%B8%8D%E6%98%AF%E5%8F%AF%E7%A9%BA%E7%B1%BB%E5%9E%8B.html&quot;&gt;为何 C# 的指针不是可空类型&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-%E5%9C%A8%E5%86%85%E5%AD%98%E4%B8%AD%E7%9A%84-double-%E7%9A%84-NAN-%E5%92%8C%E6%AD%A3%E8%B4%9F%E6%97%A0%E7%A9%B7%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%98%AF%E5%A6%82%E4%BD%95%E5%AD%98.html&quot;&gt;C# dotnet 在内存中的 double 的 NAN 和正负无穷二进制是如何存&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E8%AD%A6%E6%83%95%E7%BB%93%E6%9E%84%E4%BD%93%E5%8A%A0%E7%AD%89%E4%BA%8B%E4%BB%B6.html&quot;&gt;dotnet C# 警惕结构体加等事件&lt;/a&gt;
&lt;!-- [dotnet C# 警惕结构体加等事件 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19240284 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-await-%E9%AB%98%E7%BA%A7%E7%94%A8%E6%B3%95.html&quot;&gt;C＃ await 高级用法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E5%B1%80%E9%83%A8%E5%87%BD%E6%95%B0%E4%B8%8E%E4%BA%8B%E4%BB%B6.html&quot;&gt;C＃ 局部函数与事件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-6.0-%E5%AD%97%E7%AC%A6%E4%B8%B2-String-Interpolation.html&quot;&gt;C＃ 6.0 字符串 String Interpolation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-7.0.html&quot;&gt;C# 7.0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-7.0-%E4%BD%BF%E7%94%A8%E4%B8%8B%E5%88%92%E7%BA%BF%E5%BF%BD%E7%95%A5%E4%BD%BF%E7%94%A8%E7%9A%84%E5%8F%98%E9%87%8F.html&quot;&gt;C# 7.0 使用下划线忽略使用的变量&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BF%AE%E5%A4%8D-C-8.0-%E8%AF%AD%E6%B3%95%E7%BC%96%E8%AF%91%E5%A4%B1%E8%B4%A5.html&quot;&gt;dotnet 修复 C# 8.0 语法编译失败&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E5%9C%A8-8.0-%E5%AF%B9%E6%AF%94-string-%E5%92%8C-string_-%E7%9A%84%E7%B1%BB%E5%9E%8B.html&quot;&gt;C# 在 8.0 对比 string 和 string? 的类型&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-const-%E5%92%8C-readonly-%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB.html&quot;&gt;C# const 和 readonly 有什么区别&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E5%BC%BA%E8%BD%AC%E7%A9%BA%E4%BC%9A%E4%B8%8D%E4%BC%9A%E5%87%BA%E7%8E%B0%E5%BC%82%E5%B8%B8.html&quot;&gt;C# 强转空会不会出现异常&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E8%AD%A6%E6%83%95%E5%8F%AF%E7%A9%BA%E7%BB%93%E6%9E%84%E4%BD%93%E7%9A%84%E6%96%B9%E6%B3%95%E5%86%85%E9%83%A8%E8%B5%8B%E5%80%BC%E6%97%A0%E6%95%88.html&quot;&gt;dotnet C# 警惕可空结构体的方法内部赋值无效&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E4%BD%BF%E7%94%A8-using-%E5%85%B3%E9%94%AE%E5%AD%97%E9%87%8A%E6%94%BE-IDisposable-%E7%9A%84%E7%BB%93%E6%9E%84%E4%BD%93%E6%98%AF%E5%90%A6%E4%BC%9A%E8%A3%85%E7%AE%B1.html&quot;&gt;dotnet C# 使用 using 关键字释放 IDisposable 的结构体是否会装箱&lt;/a&gt;
&lt;!-- [dotnet C# 使用 using 关键字释放 IDisposable 的结构体是否会装箱 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18253603 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E7%BB%99%E7%BB%93%E6%9E%84%E4%BD%93%E5%AD%97%E6%AE%B5%E8%B5%8B%E5%80%BC%E9%9D%9E%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8.html&quot;&gt;dotnet C# 给结构体字段赋值非线程安全&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E7%BB%93%E6%9E%84%E4%BD%93%E5%87%BA%E6%96%B9%E6%B3%95%E5%BC%B9%E6%A0%88%E4%B9%8B%E5%90%8E%E7%9A%84%E8%A1%8C%E4%B8%BA.html&quot;&gt;dotnet C# 结构体出方法弹栈之后的行为&lt;/a&gt;
&lt;!-- [dotnet C# 结构体出方法弹栈之后的行为 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18377329 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BD%BF%E7%94%A8-LatestCSharpFeatures-%E5%BA%93%E8%AE%A9%E6%97%A7%E7%89%88%E6%9C%AC-dotnet-%E6%A1%86%E6%9E%B6%E9%A1%B9%E7%9B%AE%E4%BD%BF%E7%94%A8%E6%96%B0-C-%E8%AF%AD%E6%B3%95.html&quot;&gt;使用 LatestCSharpFeatures 库让旧版本 dotnet 框架项目使用新 C# 语法&lt;/a&gt;
&lt;!-- [使用 LatestCSharpFeatures 库让旧版本 dotnet 框架项目使用新 C# 语法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18606279 ) --&gt;&lt;/p&gt;

&lt;h2 id=&quot;dotnet&quot;&gt;dotnet&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%80%83%E5%8F%A4-dotnet-%E7%9A%84%E4%B8%8D%E5%90%8C%E7%89%88%E6%9C%AC.html&quot;&gt;考古 dotnet 的不同版本&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%BF%90%E8%A1%8C%E6%97%B6%E8%8E%B7%E5%8F%96%E6%9F%90%E7%B1%BB%E5%9E%8B%E7%9A%84%E5%AF%B9%E8%B1%A1%E5%8D%A0%E7%94%A8%E5%86%85%E5%AD%98%E5%A4%A7%E5%B0%8F.html&quot;&gt;dotnet 运行时获取某类型的对象占用内存大小&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-8.0-%E6%96%87%E4%BB%B6%E9%95%BF%E5%BA%A6-Bytes-%E5%AD%97%E8%8A%82%E8%BD%AC-KB-%E7%AD%89%E5%8D%95%E4%BD%8D%E5%AD%97%E7%AC%A6%E4%B8%B2.html&quot;&gt;C# 8.0 文件长度 Bytes 字节转 KB 等单位字符串&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%A6%82%E4%BD%95%E6%9B%B4%E6%94%B9%E5%BA%94%E7%94%A8%E5%9C%A8%E4%BB%BB%E5%8A%A1%E7%AE%A1%E7%90%86%E5%99%A8%E6%98%BE%E7%A4%BA%E7%9A%84%E8%BF%9B%E7%A8%8B%E5%90%8D-AssemblyTitle-%E7%9A%84%E5%80%BC.html&quot;&gt;dotnet 如何更改应用在任务管理器显示的进程名 AssemblyTitle 的值&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%B0%86%E6%8E%A7%E5%88%B6%E5%8F%B0-Console.WriteLine-%E5%86%85%E5%AE%B9%E8%BE%93%E5%87%BA%E5%88%B0%E6%96%87%E4%BB%B6.html&quot;&gt;dotnet 将控制台 Console.WriteLine 内容输出到文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-FileAccess-%E4%B8%8E-OpenOrCreate-%E5%AF%B9%E6%96%87%E4%BB%B6%E8%AE%BF%E9%97%AE%E6%9D%83%E9%99%90%E7%9A%84%E5%BD%B1%E5%93%8D.html&quot;&gt;dotnet 使用 FileAccess 与 OpenOrCreate 对文件访问权限的影响&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%8E%A7%E5%88%B6%E5%8F%B0%E8%AF%BB%E5%86%99-Sqlite-%E6%8F%90%E7%A4%BA-no-such-table-%E6%89%BE%E4%B8%8D%E5%88%B0%E6%96%87%E4%BB%B6.html&quot;&gt;dotnet 控制台读写 Sqlite 提示 no such table 找不到文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-%E7%BC%96%E5%86%99-curl-%E7%9A%84%E7%94%A8%E6%88%B7%E5%90%8D%E5%92%8C%E5%AF%86%E7%A0%81%E9%80%BB%E8%BE%91.html&quot;&gt;C# dotnet 编写 curl 的用户名和密码逻辑&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E4%BB%A3%E7%A0%81%E8%8E%B7%E5%8F%96%E7%B3%BB%E7%BB%9F%E6%97%A5%E5%BF%97.html&quot;&gt;dotnet C# 代码获取系统日志&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-TypeNameFormatter-%E5%BA%93%E6%A0%BC%E5%BC%8F%E5%8C%96%E8%BE%93%E5%87%BA%E5%8F%8D%E5%B0%84%E6%B3%9B%E5%9E%8B%E7%B1%BB%E5%9E%8B.html&quot;&gt;dotnet 使用 TypeNameFormatter 库格式化输出反射泛型类型&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%BF%BD%E7%95%A5%E8%BE%93%E5%87%BA%E6%96%87%E4%BB%B6%E5%A4%B9%E7%9A%84%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F.html&quot;&gt;dotnet 忽略输出文件夹的正则表达式&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E8%B0%83%E7%94%A8%E5%A7%94%E6%89%98%E7%9A%84-GetInvocationList-%E7%9A%84%E5%AF%B9%E8%B1%A1%E5%88%86%E9%85%8D.html&quot;&gt;dotnet C# 调用委托的 GetInvocationList 的对象分配&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-FormatterServices-%E7%9A%84-GetUninitializedObject-%E6%96%B9%E6%B3%95%E5%9C%A8%E4%B8%A2%E5%A4%B1-DLL-%E6%83%85%E5%86%B5%E4%B8%8B%E8%83%BD%E5%90%A6%E6%89%A7%E8%A1%8C.html&quot;&gt;dotnet 使用 FormatterServices 的 GetUninitializedObject 方法在丢失 DLL 情况下能否执行&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%85%A8%E5%B1%80-Windows-%E9%BC%A0%E6%A0%87%E9%92%A9%E5%AD%90.html&quot;&gt;dotnet C# 全局 Windows 鼠标钩子&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%8F%8D%E5%B0%84%E6%89%AB%E6%8F%8F%E7%A8%8B%E5%BA%8F%E9%9B%86%E6%89%80%E6%9C%89%E7%B1%BB%E5%9E%8B%E4%BC%9A%E4%B8%8D%E4%BC%9A%E8%A7%A6%E5%8F%91%E7%B1%BB%E5%9E%8B%E9%9D%99%E6%80%81%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0.html&quot;&gt;dotnet C# 反射扫描程序集所有类型会不会触发类型静态构造函数&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E5%86%99%E4%B8%80%E4%B8%AA24%E7%82%B9%E8%AE%A1%E7%AE%97%E7%A8%8B%E5%BA%8F.html&quot;&gt;C# 写一个24点计算程序&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E5%8F%8D%E5%B0%84%E5%88%A4%E6%96%AD%E5%B1%9E%E6%80%A7%E6%98%AF%E6%8A%BD%E8%B1%A1%E7%BB%A7%E6%89%BF.html&quot;&gt;C# 反射判断属性是抽象继承&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E7%BA%AF%E6%8E%A7%E5%88%B6%E5%8F%B0%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA%E5%85%A8%E5%B1%8F%E7%AA%97%E5%8F%A3.html&quot;&gt;C# 纯控制台创建一个全屏窗口&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E4%BD%BF%E7%94%A8%E9%98%BF%E9%87%8C%E5%87%BD%E6%95%B0%E8%AE%A1%E7%AE%97%E6%9C%8D%E5%8A%A1.html&quot;&gt;dotnet C# 使用阿里函数计算服务&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%9B%BE%E7%89%87%E7%AD%89%E6%AF%94%E9%99%90%E5%88%B6%E6%9C%80%E5%A4%A7%E5%92%8C%E6%9C%80%E5%B0%8F%E5%A4%A7%E5%B0%8F%E7%BC%A9%E6%94%BE%E7%AE%97%E6%B3%95.html&quot;&gt;dotnet C# 图片等比限制最大和最小大小缩放算法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-ConfigureAwait.Fody-%E5%BA%93%E8%AE%BE%E7%BD%AE%E9%BB%98%E8%AE%A4%E7%9A%84-await-%E5%90%8C%E6%AD%A5%E4%B8%8A%E4%B8%8B%E6%96%87%E5%88%87%E6%8D%A2%E9%85%8D%E7%BD%AE.html&quot;&gt;dotnet 使用 ConfigureAwait.Fody 库设置默认的 await 同步上下文切换配置&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Windows-Community-Toolkit-3.0-%E6%96%B0%E5%8A%9F%E8%83%BD.html&quot;&gt;Windows Community Toolkit 3.0 新功能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%B8%BA%E5%A4%A7%E5%9E%8B%E5%BA%94%E7%94%A8%E6%8E%A5%E5%85%A5-ApplicationStartupManager-%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B%E6%A1%86%E6%9E%B6.html&quot;&gt;dotnet 为大型应用接入 ApplicationStartupManager 启动流程框架&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E8%8E%B7%E5%8F%96-PC-%E5%BA%8F%E5%88%97%E5%8F%B7.html&quot;&gt;C# 获取 PC 序列号&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E5%A4%A7%E7%AB%AF%E5%B0%8F%E7%AB%AF%E8%BD%AC%E6%8D%A2.html&quot;&gt;C# 大端小端转换&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8-MemoryFailPoint-%E6%A3%80%E6%9F%A5%E6%98%AF%E5%90%A6%E6%9C%89%E8%B6%B3%E5%A4%9F%E7%9A%84%E5%86%85%E5%AD%98%E8%B5%84%E6%BA%90%E6%9D%A5%E6%89%A7%E8%A1%8C%E6%93%8D%E4%BD%9C.html&quot;&gt;dotnet C# 如何使用 MemoryFailPoint 检查是否有足够的内存资源来执行操作&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E8%8E%B7%E5%8F%96%E5%BD%93%E5%89%8D%E8%AE%BE%E5%A4%87%E5%8F%AF%E7%A7%BB%E5%8A%A8%E7%A3%81%E7%9B%98.html&quot;&gt;dotnet C# 获取当前设备可移动磁盘&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%86%99%E4%B8%80%E4%B8%AA%E6%94%AF%E6%8C%81%E5%B1%82%E5%B1%82%E7%BB%A7%E6%89%BF%E5%B1%9E%E6%80%A7%E7%9A%84%E5%AF%B9%E8%B1%A1.html&quot;&gt;dotnet 写一个支持层层继承属性的对象&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AE%A9-dotnet-%E5%91%BD%E4%BB%A4%E8%A1%8C%E8%BE%93%E5%87%BA%E4%BD%9C%E4%B8%BA%E8%8B%B1%E6%96%87%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;让 dotnet 命令行输出作为英文的方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/.NET-%E5%AE%98%E6%96%B9%E5%9B%BE%E6%A0%87.html&quot;&gt;.NET 官方图标&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/MSIL-%E9%9D%99%E6%80%81%E7%B1%BB%E5%9C%A8-IL-%E5%AE%9A%E4%B9%89%E4%B8%8A%E5%92%8C%E9%9D%9E%E9%9D%99%E6%80%81%E7%B1%BB%E7%9A%84%E5%B7%AE%E5%88%AB.html&quot;&gt;MSIL 静态类在 IL 定义上和非静态类的差别&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E7%AE%80%E5%8D%95%E5%86%99%E4%B8%80%E4%B8%AA-pdb-%E7%AC%A6%E5%8F%B7%E6%96%87%E4%BB%B6%E4%B8%8B%E8%BD%BD%E5%99%A8.html&quot;&gt;dotnet 简单写一个 pdb 符号文件下载器&lt;/a&gt;
&lt;!-- [dotnet 简单写一个 pdb 符号文件下载器 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18571309 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;代码优化&quot;&gt;代码优化&lt;/h3&gt;

&lt;p&gt;教你如何编写好的代码，如何对代码进行优化，这是入门级的博客&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BB%A3%E7%A0%81%E4%BC%98%E5%8C%96-%E8%81%8A%E8%81%8A%E9%80%BB%E8%BE%91%E5%9C%88%E5%A4%8D%E6%9D%82%E5%BA%A6.html&quot;&gt;dotnet 代码优化 聊聊逻辑圈复杂度&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;团队协作下，要求代码质量，离不开代码审查，以下是一些代码审查套路&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%B8%80%E4%BA%9B%E4%BB%A3%E7%A0%81%E5%AE%A1%E6%9F%A5%E5%A5%97%E8%B7%AF.html&quot;&gt;dotnet 一些代码审查套路&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;基础库&quot;&gt;基础库&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8%E5%AE%8C%E5%85%A8%E5%AF%B9%E8%B1%A1%E5%BC%95%E7%94%A8%E7%9B%B8%E7%AD%89%E5%88%A4%E6%96%AD.html&quot;&gt;dotnet 使用完全对象引用相等判断&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-%E6%8F%90%E7%A4%BA%E6%89%BE%E4%B8%8D%E5%88%B0-CompositionContainer-%E7%B1%BB%E7%9A%84%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95.html&quot;&gt;C# dotnet 提示找不到 CompositionContainer 类的解决方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-%E4%BD%BF%E7%94%A8-TaskCompletionSource-%E5%AE%9E%E7%8E%B0%E6%9A%82%E5%81%9C%E5%8A%9F%E8%83%BD.html&quot;&gt;C# dotnet 使用 TaskCompletionSource 实现暂停功能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%9F%BA%E7%A1%80%E4%BA%8C%E8%BF%9B%E5%88%B6%E5%A4%84%E7%90%86-%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%95%B0%E7%BB%84%E4%B8%8E%E7%BB%93%E6%9E%84%E4%BD%93%E7%9A%84%E4%BA%92%E8%BD%AC.html&quot;&gt;dotnet C# 基础二进制处理 二进制数组与结构体的互转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E6%B5%AE%E7%82%B9%E6%95%B0-double-%E7%9A%84-IsFinite-IsNormal-IsRealNumber-%E5%88%86%E4%B8%8D%E6%B8%85%E6%A5%9A.html&quot;&gt;C# 浮点数 double 的 IsFinite IsNormal IsRealNumber 分不清楚&lt;/a&gt;
&lt;!-- [C# 浮点数 double 的 IsFinite IsNormal IsRealNumber 分不清楚 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19309851 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-TypeForwarding-%E7%9A%84%E7%94%A8%E6%B3%95.html&quot;&gt;C# dotnet TypeForwarding 的用法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-GZipStream-%E5%8E%8B%E7%BC%A9%E5%AD%97%E7%AC%A6%E4%B8%B2.html&quot;&gt;dotnet 使用 GZipStream 压缩字符串&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-lz4net-%E5%8E%8B%E7%BC%A9-Stream-%E6%88%96%E6%96%87%E4%BB%B6.html&quot;&gt;dotnet 使用 lz4net 压缩 Stream 或文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%88%A4%E6%96%AD%E5%85%B6%E4%BB%96%E8%BF%9B%E7%A8%8B%E6%9C%AA%E5%93%8D%E5%BA%94.html&quot;&gt;dotnet 判断其他进程未响应&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%80%9A%E8%BF%87%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5%E7%9A%84-Scoped-%E7%BB%99%E5%B7%A5%E4%BD%9C%E6%B5%81%E6%B3%A8%E5%85%A5%E7%9B%B8%E5%90%8C%E7%9A%84%E4%B8%8A%E4%B8%8B%E6%96%87%E4%BF%A1%E6%81%AF.html&quot;&gt;dotnet 通过依赖注入的 Scoped 给工作流注入相同的上下文信息&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%89%BE%E4%B8%8D%E5%88%B0-PostAsJsonAsync-%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet 找不到 PostAsJsonAsync 方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%BB%98%E8%AE%A4%E5%88%9B%E5%BB%BA%E7%9A%84-JsonContent-%E6%B2%A1%E6%9C%89-Content-Length-%E7%9A%84%E5%86%85%E5%AE%B9%E5%A4%B4.html&quot;&gt;dotnet 默认创建的 JsonContent 没有 Content Length 的内容头&lt;/a&gt;
&lt;!-- [dotnet 默认创建的 JsonContent 没有 Content Length 的内容头 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18377448 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%AE%9A%E5%88%B6-ILogger-%E5%AE%9E%E7%8E%B0.html&quot;&gt;dotnet 定制 ILogger 实现&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%88%97%E8%A1%A8-Linq-%E7%9A%84-Take-%E7%94%A8%E6%B3%95.html&quot;&gt;dotnet 列表 Linq 的 Take 用法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E6%9E%9A%E4%B8%BE%E8%BD%AC%E5%AD%97%E7%AC%A6%E4%B8%B2.html&quot;&gt;C＃ 枚举转字符串&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%88%9B%E5%BB%BA%E4%B8%8D%E5%B8%A6BOM-%E7%9A%84UTF8.html&quot;&gt;创建不带BOM 的UTF8&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%AD%97%E5%85%B8%E7%B1%BB%E6%89%BE%E4%B8%8D%E5%88%B0-TryAdd-%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet 字典类找不到 TryAdd 方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%9D%9E%E6%B3%9B%E5%9E%8B-%E7%B1%BB%E5%9E%8B-System.Collections.IEnumerable-%E4%B8%8D%E8%83%BD%E4%B8%8E%E7%B1%BB%E5%9E%8B%E5%AE%9E%E5%8F%82%E4%B8%80%E8%B5%B7%E4%BD%BF%E7%94%A8.html&quot;&gt;dotnet 非泛型 类型 System.Collections.IEnumerable 不能与类型实参一起使用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%95%B0%E7%BB%84%E8%87%AA%E5%8A%A8%E8%BD%AC%E5%9F%BA%E7%B1%BB%E6%95%B0%E7%BB%84%E6%8F%90%E7%A4%BA-Co-variant-array-conversion-%E6%98%AF%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98.html&quot;&gt;dotnet 数组自动转基类数组提示 Co-variant array conversion 是什么问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-Find-vs-FirstOrDefault.html&quot;&gt;C＃ Find vs FirstOrDefault&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AD%A6%E6%83%95-ConcurrentDictionary-%E4%BD%BF%E7%94%A8-FirstOrDefault-%E8%8E%B7%E5%8F%96%E5%88%B0%E9%9D%9E%E9%A2%84%E6%9C%9F%E7%9A%84%E9%A6%96%E9%A1%B9.html&quot;&gt;dotnet 警惕 ConcurrentDictionary 使用 FirstOrDefault 获取到非预期的首项&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-core-%E7%94%A8%E5%80%BC%E5%88%9D%E5%A7%8B%E5%8C%96%E6%95%B4%E4%B8%AA%E6%95%B0%E7%BB%84.html&quot;&gt;dotnet core 用值初始化整个数组&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BF%AE%E5%A4%8D%E6%89%BE%E4%B8%8D%E5%88%B0-System.ServiceProcess-%E5%AE%9A%E4%B9%89.html&quot;&gt;dotnet 修复找不到 System.ServiceProcess 定义&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%91%BD%E5%90%8D%E7%AE%A1%E9%81%93%E5%90%8D%E5%AD%97%E9%95%BF%E5%BA%A6%E9%99%90%E5%88%B6.html&quot;&gt;dotnet 命名管道名字长度限制&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E7%89%B9%E6%80%A7-DynamicallyInvokable-%E6%98%AF%E7%94%A8%E6%9D%A5%E5%81%9A%E4%BB%80%E4%B9%88%E7%9A%84.html&quot;&gt;dotnet 特性 DynamicallyInvokable 是用来做什么的&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-Environment.FailFast-%E7%BB%93%E6%9D%9F%E7%A8%8B%E5%BA%8F.html&quot;&gt;dotnet 使用 Environment.FailFast 结束程序&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-GC.GetAllocatedBytesForCurrentThread-%E8%8E%B7%E5%8F%96%E5%BD%93%E5%89%8D%E7%BA%BF%E7%A8%8B%E5%88%86%E9%85%8D%E8%BF%87%E7%9A%84%E5%86%85%E5%AD%98%E5%A4%A7%E5%B0%8F.html&quot;&gt;dotnet 使用 GC.GetAllocatedBytesForCurrentThread 获取当前线程分配过的内存大小&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%B0%86%E4%BB%BB%E6%84%8F%E6%97%B6%E5%8C%BA%E7%9A%84-DateTimeOffset-%E8%BD%AC%E6%8D%A2%E4%B8%BA%E4%B8%AD%E5%9B%BD%E6%97%B6%E5%8C%BA%E6%97%B6%E9%97%B4%E6%96%87%E6%9C%AC.html&quot;&gt;dotnet 将任意时区的 DateTimeOffset 转换为中国时区时间文本&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E5%9C%A8-%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E6%B7%BB%E5%8A%A0-CallerMemberName-%E4%BC%9A%E6%80%8E%E6%A0%B7.html&quot;&gt;C# 在 构造函数添加 CallerMemberName 会怎样&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-double-%E5%A5%BD%E7%94%A8%E7%9A%84%E6%89%A9%E5%B1%95.html&quot;&gt;C# double 好用的扩展&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E7%90%86%E8%A7%A3-IConfigurationProvider-%E7%9A%84-GetChildKeys-%E6%96%B9%E6%B3%95%E7%94%A8%E9%80%94.html&quot;&gt;dotnet 理解 IConfigurationProvider 的 GetChildKeys 方法用途&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E7%BB%99-NuGet-%E5%8C%85%E5%8A%A0%E4%B8%8A-Aliases-%E5%88%AB%E5%90%8D%E8%A7%A3%E5%86%B3%E7%B1%BB%E5%9E%8B%E5%86%B2%E7%AA%81.html&quot;&gt;dotnet 给 NuGet 包加上 Aliases 别名解决类型冲突&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AF%BB-dotnet-%E6%BA%90%E4%BB%A3%E7%A0%81-%E4%B8%BA%E4%BD%95-Thread.Sleep-%E5%8D%8A%E6%AF%AB%E7%A7%92%E5%92%8C%E4%B8%80%E6%AF%AB%E7%A7%92%E7%AD%89%E5%BE%85%E6%97%B6%E9%97%B4%E5%B7%AE%E8%B7%9D%E5%A6%82%E6%AD%A4%E4%B9%8B%E5%A4%A7.html&quot;&gt;读 dotnet 源代码 为何 Thread.Sleep 半毫秒和一毫秒等待时间差距如此之大&lt;/a&gt;
&lt;!-- [读 dotnet 源代码 为何 Thread.Sleep 半毫秒和一毫秒等待时间差距如此之大 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18381792 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E4%BD%BF%E7%94%A8-System.IO.Hashing-%E5%BA%93%E8%BF%9B%E8%A1%8C-Crc32-%E8%AE%A1%E7%AE%97.html&quot;&gt;dotnet C# 使用 System.IO.Hashing 库进行 Crc32 计算&lt;/a&gt;
&lt;!-- [dotnet C# 使用 System.IO.Hashing 库进行 Crc32 计算 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18817812 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-Microsoft.NET.HostModel-%E5%BA%93%E8%BE%85%E5%8A%A9%E6%9B%B4%E6%94%B9-AppHost-%E5%86%85%E5%AE%B9.html&quot;&gt;dotnet 使用 Microsoft.NET.HostModel 库辅助更改 AppHost 内容&lt;/a&gt;
&lt;!-- [dotnet 使用 Microsoft.NET.HostModel 库辅助更改 AppHost 内容 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18897793 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%95%B0%E7%BB%84%E6%B1%A0-ArrayPool-%E8%A1%8C%E4%B8%BA%E8%AE%B0%E5%BD%95.html&quot;&gt;dotnet 数组池 ArrayPool 行为记录&lt;/a&gt;
&lt;!-- [dotnet 数组池 ArrayPool 行为记录 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18978158 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;net-framework&quot;&gt;.NET Framework&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E8%AE%BE%E5%A4%87%E5%AE%89%E8%A3%85%E4%BA%86%E5%93%AA%E4%BA%9B-.NET-Framework-%E6%A1%86%E6%9E%B6.html&quot;&gt;dotnet 获取用户设备安装了哪些 .NET Framework 框架&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-Framework-%E6%BA%90%E4%BB%A3%E7%A0%81-%E7%B1%BB%E5%BA%93%E7%9A%84%E6%84%8F%E6%80%9D.html&quot;&gt;dotnet Framework 源代码 类库的意思&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-dotnet-%E4%BD%BF%E7%94%A8%E6%9C%AC%E6%9C%BA%E6%98%A0%E5%83%8F-native-%E4%BC%98%E5%8C%96-dotnet-framework-%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%96%87%E4%BB%B6.html&quot;&gt;WPF dotnet 使用本机映像 native 优化 dotnet framework 二进制文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%90%AF%E5%8A%A8%E8%BF%9B%E7%A8%8B%E4%BC%A0%E5%85%A5%E4%B8%8D%E5%AD%98%E5%9C%A8%E7%9A%84%E6%96%87%E4%BB%B6%E5%A4%B9%E4%BD%9C%E4%B8%BA%E5%B7%A5%E4%BD%9C%E7%9B%AE%E5%BD%95%E8%A1%8C%E4%B8%BA%E5%8F%98%E6%9B%B4.html&quot;&gt;dotnet 启动进程传入不存在的文件夹作为工作目录行为变更&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;net-framework-与-net-core-差别&quot;&gt;.NET Framework 与 .NET Core 差别&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-core-%E5%92%8C-dotnet-Framework-%E5%90%AF%E5%8A%A8%E5%8F%AF%E6%89%A7%E8%A1%8C%E6%96%87%E4%BB%B6%E7%9A%84%E5%B7%AE%E5%88%AB.html&quot;&gt;dotnet core 和 dotnet Framework 启动可执行文件的差别&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%8D%87%E7%BA%A7%E5%88%B0-dotnet-core-%E4%B9%8B%E5%90%8E-HandleProcessCorruptedStateExceptions-%E6%97%A0%E6%B3%95%E6%8E%A5%E4%BD%8F%E5%BC%82%E5%B8%B8.html&quot;&gt;升级到 dotnet core 之后 HandleProcessCorruptedStateExceptions 无法接住异常&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;dotnet-core-2&quot;&gt;dotnet core 2&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-core-2-%E4%BD%BF%E7%94%A8-DispatchProxy-%E5%81%9A%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86-AOP-%E5%85%A5%E9%97%A8.html&quot;&gt;dotnet core 2 使用 DispatchProxy 做动态代理 AOP 入门&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-core-%E4%BD%BF%E7%94%A8-GBK-%E7%BC%96%E7%A0%81.html&quot;&gt;dotnet core 使用 GBK 编码&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;dotnet-5&quot;&gt;dotnet 5&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8-dotnet-5-%E9%A2%84%E8%A7%88%E7%89%88.html&quot;&gt;如何使用 dotnet 5 预览版&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%88%9A%E5%88%9A%E6%88%91%E4%BB%8E%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%9B%9E%E6%BB%9A%E4%BA%86-dotnet-5-%E7%9A%84%E7%8E%AF%E5%A2%83.html&quot;&gt;刚刚我从服务器回滚了 dotnet 5 的环境&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-5-%E4%BB%8E-IL-%E5%B1%82%E9%9D%A2%E5%88%86%E6%9E%90%E5%8D%8F%E5%8F%98%E8%BF%94%E5%9B%9E%E7%B1%BB%E5%9E%8B%E6%96%B0%E7%89%B9%E6%80%A7.html&quot;&gt;dotnet 5 从 IL 层面分析协变返回类型新特性&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-5-%E7%9A%84-bin-%E6%96%87%E4%BB%B6%E5%A4%B9%E4%B8%8B%E7%9A%84-ref-%E6%96%87%E4%BB%B6%E5%A4%B9%E6%98%AF%E5%81%9A%E4%BB%80%E4%B9%88%E7%94%A8%E7%9A%84.html&quot;&gt;dotnet 5 的 bin 文件夹下的 ref 文件夹是做什么用的&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-5-%E7%A0%B4%E5%9D%8F%E6%80%A7%E6%94%B9%E5%8A%A8-WPF-%E5%92%8C-WinForms-%E7%9A%84-OutputType-%E8%BE%93%E5%87%BA%E7%B1%BB%E5%9E%8B%E9%87%8D%E5%AE%9A%E5%90%91%E4%B8%BA-WinExe-%E7%B1%BB%E5%9E%8B.html&quot;&gt;dotnet 5 破坏性改动 WPF 和 WinForms 的 OutputType 输出类型重定向为 WinExe 类型&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-core-%E5%92%8C-.NET-5-%E4%B8%8D%E6%94%AF%E6%8C%81-Prefer32Bit-%E9%A6%96%E9%80%89-32-%E4%BD%8D%E7%9A%84%E5%8A%9F%E8%83%BD.html&quot;&gt;dotnet core 和 .NET 5 不支持 Prefer32Bit 首选 32 位的功能&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;dotnet-6&quot;&gt;dotnet 6&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-6-%E7%A0%B4%E5%9D%8F%E6%80%A7%E6%94%B9%E5%8A%A8-%E4%BB%85%E5%BC%95%E7%94%A8%E7%A8%8B%E5%BA%8F%E9%9B%86%E8%BE%93%E5%87%BA%E8%B7%AF%E5%BE%84%E5%8F%98%E6%9B%B4.html&quot;&gt;dotnet 6 破坏性改动 仅引用程序集输出路径变更&lt;/a&gt;
&lt;!-- [dotnet 6 破坏性改动 仅引用程序集输出路径变更 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18261881 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-6-%E4%BD%BF%E7%94%A8-Obfuscar-%E8%BF%9B%E8%A1%8C%E4%BB%A3%E7%A0%81%E6%B7%B7%E6%B7%86.html&quot;&gt;dotnet 6 使用 Obfuscar 进行代码混淆&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-6-%E4%BD%BF%E7%94%A8-string.Create-%E6%8F%90%E5%8D%87%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%88%9B%E5%BB%BA%E5%92%8C%E6%8B%BC%E6%8E%A5%E6%80%A7%E8%83%BD.html&quot;&gt;dotnet 6 使用 string.Create 提升字符串创建和拼接性能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-6-%E9%80%9A%E8%BF%87-DOTNET_ROOT-%E8%AE%A9%E8%B0%83%E8%B5%B7%E7%9A%84%E5%BA%94%E7%94%A8%E7%9A%84%E8%BF%9B%E7%A8%8B%E6%8B%BF%E5%88%B0%E5%85%B1%E4%BA%AB%E7%9A%84%E8%BF%90%E8%A1%8C%E6%97%B6%E6%96%87%E4%BB%B6%E5%A4%B9.html&quot;&gt;dotnet 6 通过 DOTNET_ROOT 让调起的应用的进程拿到共享的运行时文件夹&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-6-%E6%8E%A8%E8%8D%90%E4%B8%80%E4%B8%AA%E5%8F%AF%E4%BB%A3%E6%9B%BF-.NET-Remoting-%E7%9A%84-IPC-%E5%BA%93.html&quot;&gt;dotnet 6 推荐一个可代替 .NET Remoting 的 IPC 库&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-6-%E4%BD%BF%E7%94%A8-File.Exists-%E5%88%A4%E6%96%AD%E7%AE%A1%E9%81%93%E6%98%AF%E5%90%A6%E5%AD%98%E5%9C%A8%E5%B0%86%E8%AE%A9%E4%B8%8B%E6%AC%A1%E8%BF%9E%E6%8E%A5%E5%A4%B1%E8%B4%A5.html&quot;&gt;dotnet 6 使用 File.Exists 判断管道是否存在将让下次连接失败&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-Crossgen2-%E5%AF%B9-DLL-%E8%BF%9B%E8%A1%8C-ReadyToRun-%E6%8F%90%E5%8D%87%E5%90%AF%E5%8A%A8%E6%80%A7%E8%83%BD.html&quot;&gt;dotnet 使用 Crossgen2 对 DLL 进行 ReadyToRun 提升启动性能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-6-%E4%BD%BF%E7%94%A8-DependentHandle-%E5%85%B3%E8%81%94%E5%AF%B9%E8%B1%A1%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.html&quot;&gt;dotnet 6 使用 DependentHandle 关联对象生命周期&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-6-%E4%BD%BF%E7%94%A8-CreateSymbolicLink-%E5%88%9B%E5%BB%BA%E6%96%87%E4%BB%B6%E5%A4%B9%E7%AC%A6%E5%8F%B7%E9%93%BE%E6%8E%A5.html&quot;&gt;dotnet 6 使用 CreateSymbolicLink 创建文件夹符号链接&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-6-%E4%BF%AE%E5%A4%8D%E6%89%BE%E4%B8%8D%E5%88%B0-EnumeratorToEnumVariantMarshaler-%E9%97%AE%E9%A2%98.html&quot;&gt;dotnet 6 修复找不到 EnumeratorToEnumVariantMarshaler 问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-6-InterpolatedStringHandler-%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E7%9A%84-out-%E5%8F%82%E6%95%B0%E6%9C%89%E4%BB%80%E4%B9%88%E6%84%8F%E4%B9%89.html&quot;&gt;dotnet 6 InterpolatedStringHandler 构造函数的 out 参数有什么意义&lt;/a&gt;
&lt;!-- [dotnet 6 InterpolatedStringHandler 构造函数的 out 参数有什么意义 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18682800 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;dotnet-7&quot;&gt;dotnet 7&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%81%8A%E8%81%8A-dotnet-7-%E5%AF%B9-bool-%E4%B8%8E%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%BA%92%E8%BD%AC%E7%9A%84%E5%BA%95%E5%B1%82%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96.html&quot;&gt;聊聊 dotnet 7 对 bool 与字符串互转的底层性能优化&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;dotnet-8&quot;&gt;dotnet 8&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-8-%E7%A0%B4%E5%9D%8F%E6%80%A7%E6%94%B9%E5%8A%A8-%E5%9C%A8-AssemblyInformationalVersionAttribute-%E6%B7%BB%E5%8A%A0%E4%B8%8A-git-%E7%9A%84-commit-%E5%8F%B7.html&quot;&gt;dotnet 8 破坏性改动 在 AssemblyInformationalVersionAttribute 添加上 git 的 commit 号&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;dotnet-9&quot;&gt;dotnet 9&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-9-%E9%80%9A%E8%BF%87-AppHostRelativeDotNet-%E6%8C%87%E5%AE%9A%E8%87%AA%E5%AE%9A%E4%B9%89%E7%9A%84%E8%BF%90%E8%A1%8C%E6%97%B6%E8%B7%AF%E5%BE%84.html&quot;&gt;dotnet 9 通过 AppHostRelativeDotNet 指定自定义的运行时路径&lt;/a&gt;
&lt;!-- [dotnet 9 通过 AppHostRelativeDotNet 指定自定义的运行时路径 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18847625 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-9-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E9%BB%98%E8%AE%A4%E5%BC%80%E5%90%AF-CET-%E5%AF%BC%E8%87%B4%E8%BF%9B%E7%A8%8B%E5%B4%A9%E6%BA%83.html&quot;&gt;dotnet 9 已知问题 默认开启 CET 导致进程崩溃&lt;/a&gt;
&lt;!-- [dotnet 9 已知问题 默认开启 CET 导致进程崩溃 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18700406 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-Cli-sdk-%E4%BB%80%E4%B9%88%E6%98%AF-Terminal-Logger-%E4%BB%A5%E5%8F%8A%E5%A6%82%E4%BD%95%E7%A6%81%E7%94%A8%E6%AD%A4%E5%8A%9F%E8%83%BD.html&quot;&gt;dotnet Cli sdk 什么是 Terminal Logger 以及如何禁用此功能&lt;/a&gt;
&lt;!-- [dotnet Cli sdk 什么是 Terminal Logger 以及如何禁用此功能 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18764082 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E6%B5%8B%E8%AF%95-dotnet-9-%E7%9A%84-AssemblyLoadContext-%E5%8A%A8%E6%80%81%E5%8A%A0%E8%BD%BD%E5%92%8C%E5%8D%B8%E8%BD%BD%E7%A8%8B%E5%BA%8F%E9%9B%86%E8%83%BD%E5%8A%9B.html&quot;&gt;测试 dotnet 9 的 AssemblyLoadContext 动态加载和卸载程序集能力&lt;/a&gt;
&lt;!-- [测试 dotnet 9 的 AssemblyLoadContext 动态加载和卸载程序集能力 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18976101 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;序列化&quot;&gt;序列化&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-MessagePack-%E5%BA%8F%E5%88%97%E5%8C%96%E5%AF%B9%E8%B1%A1.html&quot;&gt;dotnet 使用 MessagePack 序列化对象&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;xml&quot;&gt;XML&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%BA%8F%E5%88%97%E5%8C%96-XML-%E6%97%B6%E8%BF%9B%E8%A1%8C%E8%87%AA%E5%8A%A8%E6%A0%BC%E5%BC%8F%E5%8C%96.html&quot;&gt;dotnet C# 序列化 XML 时进行自动格式化&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-core-%E8%BF%9B%E8%A1%8C-XML-%E5%BA%8F%E5%88%97%E5%8C%96%E6%8A%9B%E5%87%BA-XmlSerializers-dll-%E6%96%87%E4%BB%B6%E6%89%BE%E4%B8%8D%E5%88%B0.html&quot;&gt;dotnet core 进行 XML 序列化抛出 XmlSerializers dll 文件找不到&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E5%BA%8F%E5%88%97%E7%B1%BB%E4%B8%BA-xml-%E5%8F%AF%E4%BB%A5%E4%BD%BF%E7%94%A8%E7%9A%84%E7%89%B9%E6%80%A7%E5%A4%A7%E5%85%A8.html&quot;&gt;C# 序列类为 xml 可以使用的特性大全&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;json&quot;&gt;JSON&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-json-%E8%BD%AC-xml-%E5%AD%97%E7%AC%A6%E4%B8%B2.html&quot;&gt;C# json 转 xml 字符串&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%A6%82%E4%BD%95%E8%AE%A9-Json-%E5%BA%8F%E5%88%97%E5%8C%96%E6%95%B0%E7%BB%84%E6%97%B6%E5%BA%8F%E5%88%97%E5%8C%96%E7%BB%A7%E6%89%BF%E7%B1%BB%E7%9A%84%E5%B1%9E%E6%80%A7.html&quot;&gt;dotnet C# 如何让 Json 序列化数组时序列化继承类的属性&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%89%8B%E5%8A%A8%E8%A7%A3%E5%86%B3-json-%E8%A7%A3%E6%9E%90%E4%B8%AD%E4%B8%8D%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6%E4%B8%B2.html&quot;&gt;dotnet 手动解决 json 解析中不合法字符串&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-Newtonsoft.Json-%E8%BE%93%E5%87%BA%E6%9E%9A%E4%B8%BE%E9%A6%96%E5%AD%97%E7%AC%A6%E5%B0%8F%E5%86%99.html&quot;&gt;dotnet 使用 Newtonsoft.Json 输出枚举首字符小写&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-6-%E5%9C%A8-System.Text.Json-%E4%BD%BF%E7%94%A8-source-generation-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90%E6%8F%90%E5%8D%87-JSON-%E5%BA%8F%E5%88%97%E5%8C%96%E6%80%A7%E8%83%BD.html&quot;&gt;dotnet 6 在 System.Text.Json 使用 source generation 源代码生成提升 JSON 序列化性能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-6-%E4%BF%AE%E5%A4%8D%E5%9C%A8-System.Text.Json-%E4%BD%BF%E7%94%A8-source-generation-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90%E6%8F%90%E7%A4%BA-SYSLIB1032-%E9%94%99%E8%AF%AF.html&quot;&gt;dotnet 6 修复在 System.Text.Json 使用 source generation 源代码生成提示 SYSLIB1032 错误&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;公共组件-cbb-建设&quot;&gt;公共组件 CBB 建设&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%88%9B%E5%BB%BACBB%E5%BF%83%E5%BE%97.html&quot;&gt;创建CBB心得&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%90%AC%E9%BE%99%E5%8D%8E%E8%AE%B2%E5%85%AC%E5%85%B1%E7%BB%84%E4%BB%B6-CBB-%E5%BB%BA%E8%AE%BE%E7%AC%94%E8%AE%B0.html&quot;&gt;听龙华讲公共组件 CBB 建设笔记&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%BC%80%E6%BA%90%E5%85%AC%E5%85%B1%E7%BB%84%E4%BB%B6%E4%BB%93%E5%BA%93%E7%9A%84%E6%9B%B4%E6%96%B0%E6%97%A5%E5%BF%97%E5%BA%94%E8%AF%A5%E5%A6%82%E4%BD%95%E5%86%99.html&quot;&gt;开源公共组件仓库的更新日志应该如何写&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%A0%B9%E6%8D%AE%E5%9F%BA%E7%BA%BF%E5%8C%85%E7%89%88%E6%9C%AC%E5%AE%9E%E7%8E%B0%E5%BA%93%E7%89%88%E6%9C%AC%E5%85%BC%E5%AE%B9.html&quot;&gt;dotnet 根据基线包版本实现库版本兼容&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-CBB-%E4%B8%BA%E4%BB%80%E4%B9%88%E5%86%B3%E5%AE%9A%E6%8E%A8%E9%80%81-Tag-%E6%89%8D%E8%83%BD%E6%89%93%E5%8C%85.html&quot;&gt;dotnet CBB 为什么决定推送 Tag 才能打包&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E5%A6%82%E4%BD%95%E5%86%99%E5%87%BA%E4%B8%80%E4%B8%AA%E4%B8%8D%E8%83%BD%E8%A2%AB%E5%85%B6%E4%BB%96%E7%A8%8B%E5%BA%8F%E9%9B%86%E7%BB%A7%E6%89%BF%E7%9A%84%E6%8A%BD%E8%B1%A1%E7%B1%BB.html&quot;&gt;C# 如何写出一个不能被其他程序集继承的抽象类&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E5%9C%A8%E5%9F%BA%E7%B1%BB%E5%AE%9A%E4%B9%89%E5%A5%BD%E6%96%B9%E6%B3%95%E8%AE%A9%E5%AD%90%E7%B1%BB%E7%BB%A7%E6%89%BF%E6%8E%A5%E5%8F%A3%E5%B0%B1%E8%83%BD%E5%AE%9E%E7%8E%B0.html&quot;&gt;C# 在基类定义好方法让子类继承接口就能实现&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;插件式开发&quot;&gt;插件式开发&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-6-%E9%80%9A%E8%BF%87-DOTNET_ROOT-%E8%AE%A9%E8%B0%83%E8%B5%B7%E7%9A%84%E5%BA%94%E7%94%A8%E7%9A%84%E8%BF%9B%E7%A8%8B%E6%8B%BF%E5%88%B0%E5%85%B1%E4%BA%AB%E7%9A%84%E8%BF%90%E8%A1%8C%E6%97%B6%E6%96%87%E4%BB%B6%E5%A4%B9.html&quot;&gt;dotnet 6 通过 DOTNET_ROOT 让调起的应用的进程拿到共享的运行时文件夹&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-dnlib-%E6%A3%80%E6%B5%8B%E6%8F%92%E4%BB%B6%E7%A8%8B%E5%BA%8F%E9%9B%86%E7%9A%84-API-%E5%85%BC%E5%AE%B9%E6%80%A7.html&quot;&gt;dotnet 使用 dnlib 检测插件程序集的 API 兼容性&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;构建和打包&quot;&gt;构建和打包&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/MSBuild-%E5%B8%B8%E7%94%A8%E5%8F%82%E6%95%B0.html&quot;&gt;MSBuild 常用参数&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%A6%82%E4%BD%95%E9%80%9A%E8%BF%87%E5%91%BD%E4%BB%A4%E8%A1%8C-msbuild-%E7%BC%96%E8%AF%91%E9%A1%B9%E7%9B%AE.html&quot;&gt;如何通过命令行 msbuild 编译项目&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%BF%81%E7%A7%BB-dotnet-6-%E6%8F%90%E7%A4%BA%E5%BF%85%E9%A1%BB%E5%B0%86%E7%9B%AE%E6%A0%87%E5%B9%B3%E5%8F%B0%E8%AE%BE%E7%BD%AE%E4%B8%BA-Windows-%E5%B9%B3%E5%8F%B0.html&quot;&gt;迁移 dotnet 6 提示必须将目标平台设置为 Windows 平台&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%96%B0-sdk-style-%E9%A1%B9%E7%9B%AE%E6%A0%BC%E5%BC%8F%E7%9A%84%E4%B8%80%E4%BA%9B%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4%E5%92%8C%E5%BC%95%E7%94%A8.html&quot;&gt;dotnet 新 sdk style 项目格式的一些命名空间和引用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%96%B0-sdk-style-%E9%A1%B9%E7%9B%AE%E6%A0%BC%E5%BC%8F-%E6%96%87%E4%BB%B6%E5%86%B2%E7%AA%81.html&quot;&gt;dotnet 新 sdk style 项目格式 文件冲突&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BF%AE%E5%A4%8D-dotnet-Core-%E7%BC%BASDK%E7%BC%96%E8%AF%91%E5%A4%B1%E8%B4%A5.html&quot;&gt;修复 dotnet Core 缺SDK编译失败&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%A1%8C%E9%9D%A2%E7%AB%AF%E5%9F%BA%E4%BA%8E-AppHost-%E7%9A%84%E9%85%8D%E7%BD%AE%E5%BC%8F%E8%87%AA%E5%8A%A8%E5%88%87%E6%8D%A2%E6%9B%B4%E6%96%B0%E5%90%8E%E7%9A%84%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E8%B7%AF%E5%BE%84.html&quot;&gt;dotnet 桌面端基于 AppHost 的配置式自动切换更新后的应用程序路径&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/PublishFolderCleaner-%E8%AE%A9%E4%BD%A0%E7%9A%84-dotnet-%E5%BA%94%E7%94%A8%E5%8F%91%E5%B8%83%E6%96%87%E4%BB%B6%E5%A4%B9%E6%9B%B4%E5%8A%A0%E6%95%B4%E6%B4%81.html&quot;&gt;PublishFolderCleaner 让你的 dotnet 应用发布文件夹更加整洁&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%9E%84%E5%BB%BA%E8%BF%98%E5%8E%9F%E5%A4%B1%E8%B4%A5-NuGet.targets-%E9%94%99%E8%AF%AF%E5%8F%AF%E8%83%BD%E5%8E%9F%E5%9B%A0.html&quot;&gt;dotnet 构建还原失败 NuGet.targets 错误可能原因&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%A7%A3%E5%86%B3%E4%BD%BF%E7%94%A8%E6%9C%AC%E5%9C%B0%E4%B8%8D%E5%AE%89%E5%85%A8-http-%E7%9A%84-NuGet-%E6%BA%90-NU1803-%E8%AD%A6%E5%91%8A%E6%88%96%E6%9E%84%E5%BB%BA%E5%A4%B1%E8%B4%A5%E9%97%AE%E9%A2%98.html&quot;&gt;dotnet 解决使用本地不安全 http 的 NuGet 源 NU1803 警告或构建失败问题&lt;/a&gt;
&lt;!-- [dotnet 解决使用本地不安全 http 的 NuGet 源 NU1803 警告或构建失败问题 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18312649 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BF%AE%E5%A4%8D%E5%A4%9A%E6%A1%86%E6%9E%B6-TargetFrameworks-%E5%8C%85%E5%90%AB%E4%B8%8D%E5%8F%97%E6%94%AF%E6%8C%81%E5%B9%B3%E5%8F%B0%E5%AF%BC%E8%87%B4%E6%9E%84%E5%BB%BA%E5%A4%B1%E8%B4%A5.html&quot;&gt;dotnet 修复多框架 TargetFrameworks 包含不受支持平台导致构建失败&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-6-%E5%BC%95%E7%94%A8-NAudio-%E7%9A%84%E6%97%A7%E7%89%88%E6%9C%AC%E6%9E%84%E5%BB%BA%E4%B8%8D%E9%80%9A%E8%BF%87.html&quot;&gt;dotnet 6 引用 NAudio 的旧版本构建不通过&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-%E6%9C%AC%E5%9C%B0%E4%BB%A3%E7%A0%81%E6%9E%84%E5%BB%BA%E6%B2%A1%E9%97%AE%E9%A2%98-%E4%BD%86-CI-%E8%87%AA%E5%8A%A8%E6%9E%84%E5%BB%BA%E5%A4%B1%E8%B4%A5%E5%8F%AF%E8%83%BD%E7%9A%84%E5%8E%9F%E5%9B%A0.html&quot;&gt;C# dotnet 本地代码构建没问题，但 CI 自动构建失败可能的原因&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-core-%E6%89%93%E5%8C%85%E6%9E%84%E5%BB%BA%E6%8F%90%E7%A4%BA-MSB3024-%E5%8F%AF%E8%83%BD%E7%9A%84%E5%8E%9F%E5%9B%A0.html&quot;&gt;dotnet core 打包构建提示 MSB3024 可能的原因&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%9E%84%E5%BB%BA-SourceRoot-items-must-include-at-least-one-top-level-item-when-DeterministicSourcePaths-is-true-%E5%A4%B1%E8%B4%A5.html&quot;&gt;dotnet 构建 SourceRoot items must include at least one top-level item when DeterministicSourcePaths is true 失败&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%A7%A3%E5%86%B3-vs-%E5%87%BA%E7%8E%B0Error-MC3000-%E7%BB%99%E5%AE%9A%E7%BC%96%E7%A0%81%E4%B8%AD%E7%9A%84%E5%AD%97%E7%AC%A6%E6%97%A0%E6%95%88.html&quot;&gt;解决 vs 出现Error MC3000 给定编码中的字符无效&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/msbuild-%E4%BD%BF%E7%94%A8-ProduceOnlyReferenceAssembly-%E5%88%9B%E5%BB%BA%E4%BD%9C%E4%B8%BA%E5%BC%95%E7%94%A8%E7%9A%84%E4%BB%85%E5%85%AC%E5%BC%80%E6%88%90%E5%91%98%E7%A8%8B%E5%BA%8F%E9%9B%86.html&quot;&gt;msbuild 使用 ProduceOnlyReferenceAssembly 创建作为引用的仅公开成员程序集&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%A7%A3%E5%86%B3%E4%BB%8E%E6%97%A7%E6%A0%BC%E5%BC%8F%E7%9A%84-csproj-%E8%BF%81%E7%A7%BB%E5%88%B0%E6%96%B0%E6%A0%BC%E5%BC%8F%E7%9A%84-csproj-%E6%A0%BC%E5%BC%8F-AssemblyInfo-%E6%96%87%E4%BB%B6%E5%80%BC%E9%87%8D%E5%A4%8D%E9%97%AE%E9%A2%98.html&quot;&gt;解决从旧格式的 csproj 迁移到新格式的 csproj 格式 AssemblyInfo 文件值重复问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BF%AE%E5%A4%8D-ILLinkTasksAssembly-%E7%89%B9%E6%80%A7%E7%9A%84%E5%80%BC%E7%9A%84%E8%AE%A1%E7%AE%97%E7%BB%93%E6%9E%9C%E6%97%A0%E6%95%88.html&quot;&gt;dotnet 修复 ILLinkTasksAssembly 特性的值的计算结果无效&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%96%B0%E9%A1%B9%E7%9B%AE%E6%A0%BC%E5%BC%8F%E4%B8%8E%E5%AF%B9%E5%BA%94%E6%A1%86%E6%9E%B6%E9%A2%84%E5%AE%9A%E4%B9%89%E7%9A%84%E5%AE%8F.html&quot;&gt;dotnet 新项目格式与对应框架预定义的宏&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E6%9E%81%E9%99%90%E5%8E%8B%E7%BC%A9-dotnet-core-%E6%8E%A7%E5%88%B6%E5%8F%B0%E5%8F%91%E5%B8%83%E6%96%87%E4%BB%B6.html&quot;&gt;C# 极限压缩 dotnet core 控制台发布文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E5%B0%86dll%E6%89%93%E5%8C%85%E5%88%B0%E7%A8%8B%E5%BA%8F%E4%B8%AD.html&quot;&gt;C＃ 将dll打包到程序中&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%90%AF%E5%8A%A8-JIT-%E5%A4%9A%E6%A0%B8%E5%BF%83%E7%BC%96%E8%AF%91%E6%8F%90%E5%8D%87%E5%90%AF%E5%8A%A8%E6%80%A7%E8%83%BD.html&quot;&gt;dotnet 启动 JIT 多核心编译提升启动性能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%80%9A%E8%BF%87-PublishReadyToRunComposite-%E5%87%8F%E5%B0%91%E5%90%AF%E5%8A%A8%E8%BF%87%E7%A8%8B%E8%AF%BB%E5%8F%96%E5%A4%A7%E9%87%8F-DLL-%E6%96%87%E4%BB%B6%E6%8F%90%E5%8D%87%E5%90%AF%E5%8A%A8%E6%80%A7%E8%83%BD.html&quot;&gt;dotnet 通过 PublishReadyToRunComposite 减少启动过程读取大量 DLL 文件提升启动性能&lt;/a&gt;
&lt;!-- [dotnet 通过 PublishReadyToRunComposite 减少启动过程读取大量 DLL 文件提升启动性能 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18899676 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-%E8%A7%A3%E5%86%B3-Path-%E8%8E%B7%E5%8F%96%E6%96%87%E4%BB%B6%E5%90%8D%E8%B7%AF%E5%BE%84%E5%9C%A8-Windows-%E6%9E%84%E5%BB%BA%E5%9C%A8-Linux-%E6%89%A7%E8%A1%8C%E9%97%AE%E9%A2%98.html&quot;&gt;C# dotnet 解决 Path 获取文件名路径在 Windows 构建在 Linux 执行问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-Obsolete-%E7%89%B9%E6%80%A7%E6%A0%87%E8%AE%B0%E6%88%90%E5%91%98%E8%BF%87%E6%97%B6%E4%BF%9D%E6%8C%81%E5%BA%93%E5%92%8C%E6%A1%86%E6%9E%B6%E7%9A%84%E5%85%BC%E5%AE%B9%E6%80%A7.html&quot;&gt;dotnet 使用 Obsolete 特性标记成员过时保持库和框架的兼容性&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Ubuntu-%E9%80%9A%E8%BF%87%E6%9C%AC%E6%9C%BA%E4%BB%A3%E7%90%86%E4%BF%AE%E5%A4%8D-NuGet-%E8%BF%98%E5%8E%9F-error-NU1301-%E5%A4%B1%E8%B4%A5.html&quot;&gt;Ubuntu 通过本机代理修复 NuGet 还原 error NU1301 失败&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-core-%E5%8F%91%E5%B8%83%E5%8F%AA%E5%B8%A6%E5%BF%85%E8%A6%81%E7%9A%84%E4%BE%9D%E8%B5%96%E6%96%87%E4%BB%B6.html&quot;&gt;dotnet core 发布只带必要的依赖文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-6-%E5%91%BD%E4%BB%A4%E8%A1%8C-cmd-%E8%AE%BE%E7%BD%AE%E8%BE%93%E5%87%BA%E8%8B%B1%E6%96%87%E8%A7%A3%E5%86%B3%E4%B8%AD%E6%96%87%E4%B9%B1%E7%A0%81.html&quot;&gt;dotnet 6 命令行 cmd 设置输出英文解决中文乱码&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;aot&quot;&gt;AOT&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%B5%8B%E8%AF%95-AOT-%E7%9A%84-API-%E8%A1%8C%E4%B8%BA.html&quot;&gt;dotnet 测试 AOT 的 API 行为&lt;/a&gt;
&lt;!-- [dotnet 测试 AOT 的 API 行为 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18976102 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;多线程和锁相关&quot;&gt;多线程和锁相关&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/code-daily/p/18924622&quot;&gt;C# 锁机制全景与高效实践：从 Monitor 到 .NET 9 全新 Lock - AI·NET极客圈 - 博客园&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%A4%9A%E7%BA%BF%E7%A8%8B%E7%A6%81%E6%AD%A2%E5%90%8C%E6%97%B6%E8%B0%83%E7%94%A8%E7%9B%B8%E5%90%8C%E7%9A%84%E6%96%B9%E6%B3%95-%E7%A6%81%E6%AD%A2%E6%96%B9%E6%B3%95%E9%87%8D%E5%85%A5%E8%B0%83%E7%94%A8-%E5%8F%8C%E6%A3%80%E9%94%81%E7%9A%84%E8%AE%BE%E8%AE%A1.html&quot;&gt;dotnet 多线程禁止同时调用相同的方法 禁止方法重入调用 双检锁的设计&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-%E7%9A%84%E9%94%81-SemaphoreSlim-%E5%92%8C%E9%98%9F%E5%88%97.html&quot;&gt;C# dotnet 的锁 SemaphoreSlim 和队列&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%B5%8B%E8%AF%95-SemaphoreSlim-%E7%9A%84-Wait-%E6%98%AF%E5%90%A6%E4%BF%9D%E6%8C%81%E8%BF%9B%E5%85%A5%E7%AD%89%E5%BE%85%E7%9A%84%E9%A1%BA%E5%BA%8F%E5%85%88%E8%BF%9B%E5%85%88%E5%87%BA.html&quot;&gt;dotnet 测试 SemaphoreSlim 的 Wait 是否保持进入等待的顺序先进先出&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%87%8C%E7%9A%84%E9%82%A3%E4%BA%9B%E9%94%81-AutoResetEvent-%E7%94%A8%E6%B3%95.html&quot;&gt;dotnet 里的那些锁 AutoResetEvent 用法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-%E4%BD%BF%E7%94%A8-AsyncEx-%E5%BA%93%E7%9A%84-AsyncLock-%E5%BC%82%E6%AD%A5%E9%94%81.html&quot;&gt;C# dotnet 使用 AsyncEx 库的 AsyncLock 异步锁&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%B5%8B%E8%AF%95-Mutex-%E7%9A%84-WaitOne-%E6%98%AF%E5%90%A6%E4%BF%9D%E6%8C%81%E8%BF%9B%E5%85%A5%E7%AD%89%E5%BE%85%E7%9A%84%E9%A1%BA%E5%BA%8F%E5%85%88%E8%BF%9B%E5%85%88%E5%87%BA.html&quot;&gt;dotnet 测试 Mutex 的 WaitOne 是否保持进入等待的顺序先进先出&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-%E8%87%AA%E5%B7%B1%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E7%BA%BF%E7%A8%8B%E5%90%8C%E6%AD%A5%E4%B8%8A%E4%B8%8B%E6%96%87.html&quot;&gt;C# dotnet 自己实现一个线程同步上下文&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-%E9%AB%98%E6%80%A7%E8%83%BD%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%B7%A5%E5%85%B7-ExecuteOnceAwaiter-%E5%8F%AA%E6%89%A7%E8%A1%8C%E4%B8%80%E6%AC%A1%E7%9A%84%E4%BB%BB%E5%8A%A1.html&quot;&gt;C# dotnet 高性能多线程工具 ExecuteOnceAwaiter 只执行一次的任务&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-SemaphoreSlim-%E5%8F%AF%E8%83%BD%E7%9A%84%E5%86%85%E5%AD%98%E6%B3%84%E9%9C%B2.html&quot;&gt;dotnet 使用 SemaphoreSlim 可能的内存泄露&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-%E9%AB%98%E6%80%A7%E8%83%BD%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%B7%A5%E5%85%B7-AsyncAutoResetEvent-%E5%BC%82%E6%AD%A5%E7%AD%89%E5%BE%85%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95%E5%92%8C%E5%8E%9F%E7%90%86.html&quot;&gt;C# dotnet 高性能多线程工具 AsyncAutoResetEvent 异步等待使用方法和原理&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-%E7%BA%BF%E7%A8%8B%E4%B8%8D%E5%AE%89%E5%85%A8%E7%9A%84%E5%BC%B1%E5%BC%95%E7%94%A8%E7%BC%93%E5%AD%98.html&quot;&gt;C# dotnet 线程不安全的弱引用缓存&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E5%B0%86-Begin-%E5%92%8C-End-%E5%BC%82%E6%AD%A5%E6%96%B9%E6%B3%95%E8%BD%AC-task-%E5%BC%82%E6%AD%A5.html&quot;&gt;C# 将 Begin 和 End 异步方法转 task 异步&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-%E4%BD%BF%E7%94%A8-TaskCompletionSource-%E8%AE%A9%E4%BA%8B%E4%BB%B6%E8%BD%AC%E5%BC%82%E6%AD%A5%E6%96%B9%E6%B3%95.html&quot;&gt;C# dotnet 使用 TaskCompletionSource 让事件转异步方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E7%BA%BF%E7%A8%8B%E9%9D%99%E6%80%81%E5%AD%97%E6%AE%B5.html&quot;&gt;dotnet 线程静态字段&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%B0%A8%E6%85%8E%E5%9C%A8%E9%9D%99%E6%80%81%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E9%87%8C%E4%BD%BF%E7%94%A8%E9%94%81.html&quot;&gt;dotnet 谨慎在静态构造函数里使用锁&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-Thread.GetCurrentProcessorId-%E5%92%8C-CurrentProcess.Id-%E7%9A%84%E5%8C%BA%E5%88%AB.html&quot;&gt;C# dotnet Thread.GetCurrentProcessorId 和 CurrentProcess.Id 的区别&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%88%A4%E6%96%AD%E7%89%B9%E5%AE%9A%E8%BF%9B%E7%A8%8B%E5%AD%98%E5%9C%A8%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet 判断特定进程存在方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%8E%B7%E5%8F%96%E5%BD%93%E5%89%8D%E8%BF%9B%E7%A8%8B%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet 获取当前进程方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%A4%9A%E7%BA%BF%E7%A8%8B%E9%9B%86%E5%90%88%E7%9A%84-Linq-%E8%8E%B7%E5%8F%96%E5%80%BC%E5%90%8C%E6%97%B6%E5%86%99%E5%85%A5%E9%9B%86%E5%90%88%E5%B0%86%E4%BC%9A%E6%8A%9B%E5%87%BA%E5%BC%82%E5%B8%B8.html&quot;&gt;dotnet C# 多线程集合的 Linq 获取值同时写入集合将会抛出异常&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-Task.Run-%E5%92%8C-Task.Factory.StartNew-%E5%8C%BA%E5%88%AB.html&quot;&gt;C# Task.Run 和 Task.Factory.StartNew 区别&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AD%A6%E6%83%95-Task-%E7%9A%84-ContinueWith-%E5%B8%A6%E4%B8%8A-OnlyOnFaulted-%E5%8F%82%E6%95%B0%E6%8A%9B%E5%87%BA%E5%8F%96%E6%B6%88%E5%BC%82%E5%B8%B8.html&quot;&gt;dotnet 警惕 Task 的 ContinueWith 带上 OnlyOnFaulted 参数抛出取消异常&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-TaskTupleAwaiter-%E5%90%8C%E6%97%B6%E7%AD%89%E5%BE%85%E5%A4%9A%E4%B8%AA%E4%BB%BB%E5%8A%A1%E7%AE%80%E5%8C%96%E4%BB%A3%E7%A0%81%E5%86%99%E6%B3%95.html&quot;&gt;dotnet 使用 TaskTupleAwaiter 同时等待多个任务简化代码写法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E7%9A%84-TaskCompletionSource-%E7%9A%84-TrySetResult-%E6%98%AF%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8.html&quot;&gt;dotnet 的 TaskCompletionSource 的 TrySetResult 是线程安全&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AE%B0-TaskCompletionSource-%E7%9A%84-SetException-%E5%8F%AF%E8%83%BD%E5%B0%86%E5%BC%82%E5%B8%B8%E8%AE%B0%E5%BD%95%E5%88%B0-UnobservedTaskException-%E7%9A%84%E9%97%AE%E9%A2%98.html&quot;&gt;dotnet 记 TaskCompletionSource 的 SetException 可能将异常记录到 UnobservedTaskException 的问题&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;多进程&quot;&gt;多进程&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-6-%E5%88%9B%E5%BB%BA%E8%BF%9B%E7%A8%8B-Process.Start-%E6%97%B6%E8%AE%BE%E7%BD%AE-UseShellExecute-%E5%9C%A8-Windows-%E4%B8%8B%E5%AF%B9%E6%80%A7%E8%83%BD%E7%9A%84%E5%BD%B1%E5%93%8D.html&quot;&gt;dotnet 6 创建进程 Process.Start 时设置 UseShellExecute 在 Windows 下对性能的影响&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;多进程通讯&quot;&gt;多进程通讯&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-6-%E6%8E%A8%E8%8D%90%E4%B8%80%E4%B8%AA%E5%8F%AF%E4%BB%A3%E6%9B%BF-.NET-Remoting-%E7%9A%84-IPC-%E5%BA%93.html&quot;&gt;dotnet 6 推荐一个可代替 .NET Remoting 的 IPC 库&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%8E%A8%E8%8D%90%E4%B8%80%E4%B8%AA%E4%BD%BF%E7%94%A8-Json-%E7%9B%B4%E6%8E%A5%E8%B7%AF%E7%94%B1%E9%80%9A%E8%AE%AF%E7%9A%84-IPC-%E5%BA%93.html&quot;&gt;dotnet 推荐一个使用 Json 直接路由通讯的 IPC 库&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E7%94%A8-MVC-%E7%9A%84%E6%96%B9%E5%BC%8F%E6%89%93%E5%BC%80-IPC-%E5%91%BD%E5%90%8D%E7%AE%A1%E9%81%93.html&quot;&gt;dotnet 用 MVC 的方式打开 IPC 命名管道&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%95%99%E4%BD%A0%E5%86%99%E4%B8%80%E4%B8%AA%E5%8F%AF%E4%BB%A5%E6%90%9E%E7%82%B8%E6%9C%AC%E6%9C%BA%E6%89%80%E6%9C%89-WCF-%E5%BA%94%E7%94%A8%E7%9A%84%E7%A8%8B%E5%BA%8F%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet 教你写一个可以搞炸本机所有 WCF 应用的程序方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dot-net-core-%E4%BD%BF%E7%94%A8-IPC-%E8%BF%9B%E7%A8%8B%E9%80%9A%E4%BF%A1.html&quot;&gt;dot net core 使用 IPC 进程通信&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;性能优化&quot;&gt;性能优化&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-%E4%BB%8E%E5%90%8E%E5%90%91%E5%89%8D%E5%88%A0%E9%99%A4%E5%88%97%E8%A1%A8%E5%85%83%E7%B4%A0%E6%8F%90%E5%8D%87%E6%80%A7%E8%83%BD%E7%9A%84%E5%8E%9F%E7%90%86.html&quot;&gt;C# dotnet 从后向前删除列表元素提升性能的原理&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-%E4%BD%BF%E7%94%A8-startIndex-%E6%8F%90%E5%8D%87-IndexOf-%E7%9A%84%E6%80%A7%E8%83%BD.html&quot;&gt;C# dotnet 使用 startIndex 提升 IndexOf 的性能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-7.2-%E9%80%9A%E8%BF%87-in-%E5%92%8C-readonly-struct-%E5%87%8F%E5%B0%91%E6%96%B9%E6%B3%95%E5%80%BC%E5%A4%8D%E5%88%B6%E6%8F%90%E9%AB%98%E6%80%A7%E8%83%BD.html&quot;&gt;C# 7.2 通过 in 和 readonly struct 减少方法值复制提高性能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-Span-%E5%85%A5%E9%97%A8.html&quot;&gt;C# Span 入门&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-ValueTask-%E7%AE%80%E5%8D%95%E5%85%A5%E9%97%A8.html&quot;&gt;dotnet ValueTask 简单入门&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-Interlocked-%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E6%97%A0%E9%94%81%E7%9A%84%E5%BF%AB%E9%80%9F%E6%97%A0%E5%BA%8F%E4%BB%85%E5%86%99%E9%9B%86%E5%90%88.html&quot;&gt;dotnet 使用 Interlocked 实现一个无锁的快速无序仅写集合&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96-%E5%88%A9%E7%94%A8%E5%93%88%E5%B8%8C%E6%80%9D%E6%83%B3%E4%BC%98%E5%8C%96%E5%A4%A7%E5%AF%B9%E8%B1%A1%E9%9B%86%E5%90%88%E7%9B%B8%E7%AD%89%E5%88%A4%E6%96%AD%E6%80%A7%E8%83%BD.html&quot;&gt;dotnet 性能优化 利用哈希思想优化大对象集合相等判断性能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E4%BD%BF%E7%94%A8%E6%97%A0%E6%8D%95%E8%8E%B7%E7%9A%84%E5%A7%94%E6%89%98%E5%8F%AF%E4%BB%A5%E8%8E%B7%E5%BE%97%E7%BC%96%E8%AF%91%E5%99%A8%E7%BC%93%E5%AD%98%E5%87%8F%E5%B0%91%E5%AF%B9%E8%B1%A1%E5%88%9B%E5%BB%BA.html&quot;&gt;dotnet C# 使用无捕获的委托可以获得编译器缓存减少对象创建&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E4%BD%BF%E7%94%A8-EqualityComparer-%E6%8F%90%E5%8D%87%E6%B3%9B%E5%9E%8B%E5%80%BC%E7%B1%BB%E5%9E%8B%E7%9B%B8%E7%AD%89%E5%88%A4%E6%96%AD%E6%80%A7%E8%83%BD.html&quot;&gt;dotnet C# 使用 EqualityComparer 提升泛型值类型相等判断性能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%AF%B9%E6%8C%87%E9%92%88%E8%BD%AC%E6%8D%A2%E4%B8%BA%E7%BB%93%E6%9E%84%E4%BD%93%E5%A4%9A%E4%B8%AA%E4%B8%8D%E5%90%8C%E6%96%B9%E6%B3%95%E7%9A%84%E6%80%A7%E8%83%BD%E5%88%86%E6%9E%90.html&quot;&gt;dotnet 对指针转换为结构体多个不同方法的性能分析&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-ToUpperInvariant-%E6%9B%BF%E6%8D%A2-ToUpper-%E4%BB%A5%E9%81%BF%E5%85%8D%E5%88%9D%E5%A7%8B%E5%8C%96-icu-%E8%BF%87%E6%85%A2.html&quot;&gt;dotnet 使用 ToUpperInvariant 替换 ToUpper 以避免初始化 icu 过慢&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;网络&quot;&gt;网络&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-6-%E4%B8%BA%E4%BB%80%E4%B9%88%E7%BD%91%E7%BB%9C%E8%AF%B7%E6%B1%82%E4%B8%8D%E8%B7%9F%E9%9A%8F%E7%B3%BB%E7%BB%9F%E7%BD%91%E7%BB%9C%E4%BB%A3%E7%90%86%E5%8F%98%E5%8C%96%E8%80%8C%E5%8A%A8%E6%80%81%E5%88%87%E6%8D%A2%E4%BB%A3%E7%90%86.html&quot;&gt;dotnet 6 为什么网络请求不跟随系统网络代理变化而动态切换代理&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-6-HttpClientHandler-%E5%92%8C-SocketsHttpHandler-%E6%9C%89%E4%BB%80%E4%B9%88%E5%B7%AE%E5%88%AB.html&quot;&gt;dotnet 6 HttpClientHandler 和 SocketsHttpHandler 有什么差别&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%98%AF%E5%90%A6%E5%BA%94%E8%AF%A5%E5%AF%B9-HttpResponseMessage-%E8%B0%83%E7%94%A8-Dispose-%E8%BF%9B%E8%A1%8C%E9%87%8A%E6%94%BE.html&quot;&gt;dotnet 是否应该对 HttpResponseMessage 调用 Dispose 进行释放&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-6-%E4%BD%BF%E7%94%A8-HttpWebRequest-%E8%BF%9B%E8%A1%8C-POST-%E6%96%87%E4%BB%B6%E5%B0%86%E5%8D%A0%E7%94%A8%E5%A4%A7%E9%87%8F%E5%86%85%E5%AD%98.html&quot;&gt;dotnet 6 使用 HttpWebRequest 进行 POST 文件将占用大量内存&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-6-%E4%BD%BF%E7%94%A8-HttpClient-%E7%9A%84%E8%B6%85%E6%97%B6%E6%9C%BA%E5%88%B6.html&quot;&gt;dotnet 6 使用 HttpClient 的超时机制&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-6-%E7%B2%BE%E7%BB%86%E6%8E%A7%E5%88%B6-HttpClient-%E7%BD%91%E7%BB%9C%E8%AF%B7%E6%B1%82%E8%B6%85%E6%97%B6.html&quot;&gt;dotnet 6 精细控制 HttpClient 网络请求超时&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-6-%E5%9C%A8-Win7-%E7%B3%BB%E7%BB%9F%E8%AF%81%E4%B9%A6%E9%93%BE%E9%94%99%E8%AF%AF%E5%AF%BC%E8%87%B4-HttpWebRequest-%E5%86%85%E5%AD%98%E6%B3%84%E9%9C%B2.html&quot;&gt;dotnet 6 在 Win7 系统证书链错误导致 HttpWebRequest 内存泄露&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/NewLife-%E7%9A%84-RocketMQ-%E7%9A%84%E7%94%9F%E4%BA%A7%E8%80%85%E6%AF%8F%E6%AC%A1%E9%83%BD%E6%98%AF%E6%96%B0%E5%AE%9E%E4%BE%8B%E5%B0%86%E5%8F%AA%E7%94%B1%E4%B8%80%E4%B8%AA%E6%B6%88%E8%B4%B9%E8%80%85%E6%B6%88%E8%B4%B9.html&quot;&gt;NewLife 的 RocketMQ 的生产者每次都是新实例将只由一个消费者消费&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%A6%82%E4%BD%95%E4%B8%8D%E8%81%94%E7%BD%91%E4%B8%8B%E5%BF%AB%E9%80%9F%E8%8E%B7%E5%8F%96%E4%B8%80%E6%AE%B5url%E9%93%BE%E6%8E%A5%E9%87%8C%E9%9D%A2%E8%AF%BB%E5%8F%96%E6%96%87%E4%BB%B6%E5%90%8D.html&quot;&gt;dotnet C# 如何不联网下快速获取一段url链接里面读取文件名&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-core-%E5%B1%80%E5%9F%9F%E7%BD%91%E7%BB%84%E6%92%AD%E6%96%B9%E6%B3%95.html&quot;&gt;C# dotnet core 局域网组播方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-%E8%8E%B7%E5%8F%96%E6%95%B4%E4%B8%AA%E5%B1%80%E5%9F%9F%E7%BD%91%E7%9A%84-ip-%E5%9C%B0%E5%9D%80.html&quot;&gt;C# dotnet 获取整个局域网的 ip 地址&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E8%8E%B7%E5%8F%96%E6%9C%AC%E6%9C%BA%E5%A4%96%E7%BD%91-IP-%E5%9C%B0%E5%9D%80.html&quot;&gt;dotnet C# 获取本机外网 IP 地址&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-core-%E8%8E%B7%E5%8F%96-MacAddress-%E5%9C%B0%E5%9D%80%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet core 获取 MacAddress 地址方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%80%9A%E8%BF%87-HttpClient-%E4%B8%8B%E8%BD%BD%E6%96%87%E4%BB%B6%E5%90%8C%E6%97%B6%E6%8A%A5%E5%91%8A%E8%BF%9B%E5%BA%A6%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet 通过 HttpClient 下载文件同时报告进度的方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%85%B3%E4%BA%8E%E5%A4%9A%E4%B8%AA-Cookie-%E7%9A%84%E5%88%86%E9%9A%94%E7%AC%A6%E8%BF%99%E4%BB%B6%E4%BA%8B.html&quot;&gt;关于多个 Cookie 的分隔符这件事&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%BC%80%E5%90%AF-Fiddler-%E6%8A%93%E5%8C%85%E5%B0%86%E4%BC%9A%E8%AE%A9%E8%AF%B7%E6%B1%82-HOST-%E5%A4%B4%E8%A2%AB%E6%9B%B4%E6%94%B9.html&quot;&gt;dotnet 开启 Fiddler 抓包将会让请求 HOST 头被更改&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%A7%A3%E5%86%B3-System.Net.Sockets.SocketException-10106-%E6%97%A0%E6%B3%95%E5%8A%A0%E8%BD%BD%E6%88%96%E5%88%9D%E5%A7%8B%E5%8C%96%E8%AF%B7%E6%B1%82%E7%9A%84%E6%9C%8D%E5%8A%A1%E6%8F%90%E4%BE%9B%E7%A8%8B%E5%BA%8F-%E6%97%A0%E6%B3%95%E8%81%94%E7%BD%91.html&quot;&gt;解决 System.Net.Sockets.SocketException 10106 无法加载或初始化请求的服务提供程序 无法联网&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%A7%A3%E5%86%B3-System.Net.Sockets.SocketException-10045-%E5%8F%82%E8%80%83%E7%9A%84%E5%AF%B9%E8%B1%A1%E7%B1%BB%E5%9E%8B%E4%B8%8D%E6%94%AF%E6%8C%81%E5%B0%9D%E8%AF%95%E7%9A%84%E6%93%8D%E4%BD%9C-%E6%97%A0%E6%B3%95%E8%81%94%E7%BD%91.html&quot;&gt;解决 System.Net.Sockets.SocketException 10045 参考的对象类型不支持尝试的操作 无法联网&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-core-%E4%B8%8D%E8%87%AA%E5%8A%A8%E4%BB%8E-https-%E5%88%B0-http-%E7%9A%84-302-%E9%87%8D%E5%AE%9A%E5%90%91.html&quot;&gt;dotnet core 不自动从 https 到 http 的 302 重定向&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet6-C-%E4%B8%80%E4%B8%AA%E5%9B%BD%E5%86%85%E8%BF%98%E8%83%BD%E7%94%A8%E7%9A%84-NTP-%E6%97%B6%E9%97%B4%E6%A0%A1%E5%87%86%E5%AE%A2%E6%88%B7%E7%AB%AF%E7%9A%84%E5%AE%9E%E7%8E%B0.html&quot;&gt;dotnet6 C# 一个国内还能用的 NTP 时间校准客户端的实现&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%85%A5%E9%97%A8%E7%A4%BA%E4%BE%8B-%E7%94%A8%E5%BA%95%E5%B1%82%E7%9A%84-Socket-%E8%BF%9B%E8%A1%8C-HTTP-%E7%BD%91%E7%BB%9C%E8%AF%B7%E6%B1%82.html&quot;&gt;dotnet C# 入门示例 用底层的 Socket 进行 HTTP 网络请求&lt;/a&gt;
&lt;!-- [dotnet C# 入门示例 用底层的 Socket 进行 HTTP 网络请求 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18932217 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;文件读写&quot;&gt;文件读写&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%96%87%E4%BB%B6%E8%AF%BB%E5%86%99%E5%8A%A1%E5%BF%85%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.html&quot;&gt;dotnet 文件读写务必注意事项&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E7%AE%80%E5%8D%95%E8%AF%BB%E5%8F%96%E6%96%87%E4%BB%B6.html&quot;&gt;C# 简单读取文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-%E4%BD%BF%E7%94%A8-FileStream-%E9%9A%8F%E6%9C%BA%E6%96%87%E4%BB%B6%E8%AF%BB%E5%86%99.html&quot;&gt;C# dotnet 使用 FileStream 随机文件读写&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-%E4%BD%BF%E7%94%A8%E5%88%A4%E6%96%AD%E6%96%87%E4%BB%B6%E5%A4%B9%E5%AD%98%E5%9C%A8%E7%9A%84%E6%96%B9%E6%B3%95%E5%88%A4%E6%96%AD%E4%B8%80%E4%B8%AA%E6%96%87%E4%BB%B6%E8%B7%AF%E5%BE%84%E4%BC%9A%E6%80%8E%E6%A0%B7.html&quot;&gt;C# dotnet 使用判断文件夹存在的方法判断一个文件路径会怎样&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E4%B8%8D%E8%83%BD%E7%94%A8%E4%BA%8E%E6%96%87%E4%BB%B6%E5%90%8D%E7%9A%84%E5%AD%97%E7%AC%A6.html&quot;&gt;C# 不能用于文件名的字符&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-%E5%B0%86-Stream-%E4%BF%9D%E5%AD%98%E5%88%B0%E6%96%87%E4%BB%B6%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;C# dotnet 将 Stream 保存到文件的方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%88%A0%E9%99%A4%E6%96%87%E4%BB%B6%E5%A4%B9%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet 删除文件夹方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%B0%86%E6%96%87%E4%BB%B6%E5%88%A0%E9%99%A4%E5%88%B0%E5%9B%9E%E6%94%B6%E7%AB%99.html&quot;&gt;dotnet 将文件删除到回收站&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%88%A0%E9%99%A4%E5%8F%AA%E8%AF%BB%E6%96%87%E4%BB%B6.html&quot;&gt;dotnet 删除只读文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E7%9B%B8%E5%AF%B9%E8%B7%AF%E5%BE%84%E8%BD%AC%E7%BB%9D%E5%AF%B9%E8%B7%AF%E5%BE%84.html&quot;&gt;C＃ 相对路径转绝对路径&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AD%A6%E6%83%95%E5%88%A4%E6%96%AD%E6%96%87%E4%BB%B6%E6%98%AF%E5%90%A6%E5%AD%98%E5%9C%A8%E5%9B%A0%E4%B8%BA%E6%A3%80%E6%9F%A5%E7%BD%91%E7%BB%9C%E8%B5%84%E6%BA%90%E9%80%A0%E6%88%90%E8%B6%85%E9%95%BF%E7%AD%89%E5%BE%85.html&quot;&gt;dotnet 警惕判断文件是否存在因为检查网络资源造成超长等待&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E5%88%A4%E6%96%AD%E6%96%87%E4%BB%B6%E7%BC%96%E7%A0%81.html&quot;&gt;C＃ 判断文件编码&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E5%88%A4%E6%96%AD%E6%96%87%E4%BB%B6%E5%B1%9E%E4%BA%8E%E6%96%87%E6%9C%AC%E6%88%96%E4%BA%8C%E8%BF%9B%E5%88%B6.html&quot;&gt;C＃判断文件属于文本或二进制&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;平台调用&quot;&gt;平台调用&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-CsWin32-%E5%BA%93%E7%AE%80%E5%8C%96-Win32-%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8%E9%80%BB%E8%BE%91.html&quot;&gt;dotnet 使用 CsWin32 库简化 Win32 函数调用逻辑&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E6%8E%A8%E8%8D%90%E5%AE%98%E6%96%B9%E5%BC%80%E6%BA%90-PInvoke-%E5%BA%93-%E5%8C%85%E5%90%AB%E5%A4%A7%E9%87%8F-win32-%E5%B0%81%E8%A3%85.html&quot;&gt;推荐官方开源 PInvoke 库 包含大量 win32 封装&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E4%BD%BF%E7%94%A8-Win32-%E5%87%BD%E6%95%B0%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E4%B8%8B%E8%BD%BD%E6%96%87%E4%BB%B6%E5%A4%B9%E7%9A%84%E8%B7%AF%E5%BE%84%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet C# 使用 Win32 函数获取用户下载文件夹的路径的方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E6%BC%94%E7%BB%83-dotnet-%E4%BD%BF%E7%94%A8-GeneratedComInterface-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90%E6%96%B9%E5%BC%8F%E8%B0%83%E7%94%A8-COM-%E6%8E%A5%E5%8F%A3.html&quot;&gt;演练 dotnet 使用 GeneratedComInterface 源代码生成方式调用 COM 接口&lt;/a&gt;
&lt;!-- [演练 dotnet 使用 GeneratedComInterface 源代码生成方式调用 COM 接口 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18684914 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E6%BC%94%E7%BB%83-dotnet-%E4%BD%BF%E7%94%A8-%E5%87%BD%E6%95%B0%E6%8C%87%E9%92%88-%E8%B0%83%E7%94%A8-COM-%E6%8E%A5%E5%8F%A3.html&quot;&gt;演练 dotnet 使用 函数指针 调用 COM 接口&lt;/a&gt;
&lt;!-- [演练 dotnet 使用 函数指针 调用 COM 接口 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18684913 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;原理博客&quot;&gt;原理博客&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E6%8E%A2%E7%B4%A2-dotnet-core-%E4%B8%BA%E4%BD%95%E5%9C%A8-Windows7-%E7%B3%BB%E7%BB%9F%E9%9C%80%E8%A6%81%E8%A1%A5%E4%B8%81%E7%9A%84%E5%8E%9F%E5%9B%A0.html&quot;&gt;探索 dotnet core 为何在 Windows7 系统需要补丁的原因&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-core-%E5%BA%94%E7%94%A8%E6%98%AF%E5%A6%82%E4%BD%95%E8%B7%91%E8%B5%B7%E6%9D%A5%E7%9A%84-%E9%80%9A%E8%BF%87AppHost%E7%90%86%E8%A7%A3%E8%BF%90%E8%A1%8C%E8%BF%87%E7%A8%8B.html&quot;&gt;dotnet core 应用是如何跑起来的 通过AppHost理解运行过程&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0-dotnet-%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%9C%A8%E5%86%85%E5%AD%98%E6%98%AF%E5%A6%82%E4%BD%95%E5%AD%98%E6%94%BE.html&quot;&gt;读书笔记 dotnet 的字符串在内存是如何存放&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-core-%E5%BA%94%E7%94%A8%E6%98%AF%E5%A6%82%E4%BD%95%E8%B7%91%E8%B5%B7%E6%9D%A5%E7%9A%84-%E9%80%9A%E8%BF%87%E8%87%AA%E5%B7%B1%E5%86%99%E4%B8%80%E4%B8%AA-dotnet-host-%E7%90%86%E8%A7%A3%E8%BF%90%E8%A1%8C%E8%BF%87%E7%A8%8B.html&quot;&gt;dotnet core 应用是如何跑起来的 通过自己写一个 dotnet host 理解运行过程&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-CLR-%E8%81%8A%E8%81%8A%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%86%85%E5%AD%98%E5%B8%83%E5%B1%80-%E4%B8%80%E4%B8%AA%E7%A9%BA%E5%AF%B9%E8%B1%A1%E5%8D%A0%E7%94%A8%E5%A4%9A%E5%B0%91%E5%86%85%E5%AD%98.html&quot;&gt;C# CLR 聊聊对象的内存布局 一个空对象占用多少内存&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0-dotnet-%E4%BB%80%E4%B9%88%E6%97%B6%E5%80%99%E8%BF%9B%E8%A1%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6.html&quot;&gt;读书笔记 dotnet 什么时候进行垃圾回收&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-ConditionalWeakTable-%E7%9A%84%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86.html&quot;&gt;dotnet ConditionalWeakTable 的底层原理&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0-dotnet-%E5%A4%A7%E5%AF%B9%E8%B1%A1%E5%A0%86%E5%92%8C%E5%B0%8F%E5%AF%B9%E8%B1%A1%E5%A0%86.html&quot;&gt;读书笔记 dotnet 大对象堆和小对象堆&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-core-%E9%BB%91%E7%A7%91%E6%8A%80-String.IndexOf-%E6%80%A7%E8%83%BD.html&quot;&gt;dotnet core 黑科技·String.IndexOf 性能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-5-%E4%BB%8E-IL-%E5%B1%82%E9%9D%A2%E5%88%86%E6%9E%90%E5%8D%8F%E5%8F%98%E8%BF%94%E5%9B%9E%E7%B1%BB%E5%9E%8B%E6%96%B0%E7%89%B9%E6%80%A7.html&quot;&gt;dotnet 5 从 IL 层面分析协变返回类型新特性&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E4%BB%A3%E7%A0%81%E5%8D%A0%E7%94%A8%E7%9A%84%E7%A9%BA%E9%97%B4.html&quot;&gt;C# 代码占用的空间&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-ValueTuple-%E5%8E%9F%E7%90%86.html&quot;&gt;C# ValueTuple 原理&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;设计规范&quot;&gt;设计规范&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-core-%E7%BC%96%E7%A8%8B%E8%A7%84%E8%8C%83.html&quot;&gt;dotnet core 编程规范&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AE%BE%E8%AE%A1%E8%A7%84%E8%8C%83-%E6%95%B0%E7%BB%84%E5%AE%9A%E4%B9%89.html&quot;&gt;dotnet 设计规范 · 数组定义&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AE%BE%E8%AE%A1%E8%A7%84%E8%8C%83-%E6%8A%BD%E8%B1%A1%E5%AE%9A%E4%B9%89.html&quot;&gt;dotnet 设计规范 · 抽象定义&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AE%BE%E8%AE%A1%E8%A7%84%E8%8C%83-%E6%8A%BD%E8%B1%A1%E7%B1%BB.html&quot;&gt;dotnet 设计规范 · 抽象类&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AE%BE%E8%AE%A1%E8%A7%84%E8%8C%83-%E7%BB%93%E6%9E%84%E4%BD%93%E5%AE%9A%E4%B9%89.html&quot;&gt;dotnet 设计规范 · 结构体定义&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%96%B9%E6%B3%95%E5%90%8D-To-%E5%92%8C-As-%E6%9C%89%E4%BB%80%E4%B9%88%E4%B8%8D%E5%90%8C.html&quot;&gt;dotnet 方法名 To 和 As 有什么不同&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%8D%E5%BB%BA%E8%AE%AE%E5%AF%B9%E4%BB%BB%E6%84%8F%E7%9A%84-IEnumerable-%E5%8F%8D%E8%BD%AC.html&quot;&gt;C# 为什么不建议对任意的 IEnumerable 反转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%B8%BA%E4%BB%80%E4%B9%88%E5%BC%80%E6%BA%90%E7%9A%84%E8%BF%90%E8%A1%8C%E6%97%B6%E4%BB%93%E5%BA%93%E4%BB%A3%E7%A0%81%E5%87%8F%E5%B0%91%E4%BD%BF%E7%94%A8-Linq-%E8%AF%AD%E5%8F%A5.html&quot;&gt;dotnet 为什么开源的运行时仓库代码减少使用 Linq 语句&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;工具方法&quot;&gt;工具方法&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%89%BE%E5%88%B0%E5%8D%9A%E5%AE%A2%E4%B8%AD%E5%BC%95%E7%94%A8%E5%B7%B2%E5%A4%B1%E8%B4%A5%E7%9A%84%E9%93%BE%E6%8E%A5%E5%9C%B0%E5%9D%80.html&quot;&gt;dotnet 找到博客中引用已失败的链接地址&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-%E8%8E%B7%E5%8F%96%E6%9F%90%E4%B8%AA%E5%AD%97%E7%AC%A6%E6%89%80%E5%9C%A8-Unicode-%E5%AD%97%E7%AC%A6%E5%B9%B3%E9%9D%A2%E6%98%A0%E5%B0%84.html&quot;&gt;C# dotnet 获取某个字符所在 Unicode 字符平面映射&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E7%94%A8-C-%E5%86%99%E8%84%9A%E6%9C%AC-%E5%A6%82%E4%BD%95%E8%BE%93%E5%87%BA%E6%96%87%E4%BB%B6%E5%A4%B9%E5%86%85%E6%89%80%E6%9C%89%E6%96%87%E4%BB%B6%E5%90%8D.html&quot;&gt;用 C# 写脚本 如何输出文件夹内所有文件名&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%BF%9B%E8%A1%8C%E4%BA%8C%E8%BF%9B%E5%88%B6%E5%B7%AE%E5%88%86%E5%8E%8B%E7%BC%A9%E6%96%87%E4%BB%B6.html&quot;&gt;dotnet 进行二进制差分压缩文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E7%AE%80%E5%8D%95%E7%9A%84%E8%BF%BD%E5%8A%A0%E6%96%87%E4%BB%B6%E5%A4%B9%E5%88%B0-ZipArchive-%E5%8E%8B%E7%BC%A9%E6%96%87%E4%BB%B6%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet C# 简单的追加文件夹到 ZipArchive 压缩文件的方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-%E5%88%9B%E5%BB%BA%E5%AF%B9%E8%B1%A1%E9%99%84%E5%8A%A0%E5%B1%9E%E6%80%A7%E5%AE%9A%E4%B9%89-%E6%94%AF%E6%8C%81%E9%99%84%E5%8A%A0%E4%BB%BB%E6%84%8F%E7%B1%BB%E5%9E%8B.html&quot;&gt;C# dotnet 创建对象附加属性定义 支持附加任意类型&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-%E5%88%86%E5%89%B2%E6%8D%A2%E8%A1%8C.html&quot;&gt;C# dotnet 分割换行&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%80%9A%E8%BF%87-dotnetCampus.YamlToCsharp-%E5%B0%86-YAML-%E5%A4%9A%E8%AF%AD%E8%A8%80%E6%96%87%E4%BB%B6%E6%9E%84%E5%BB%BA%E4%B8%BA%E4%BB%A3%E7%A0%81.html&quot;&gt;dotnet 通过 dotnetCampus.YamlToCsharp 将 YAML 多语言文件构建为代码&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-%E4%B8%80%E4%B8%AA%E7%9C%8B%E4%B8%8A%E5%8E%BB%E8%BF%98%E8%83%BD%E7%94%A8%E7%9A%84%E4%BA%8C%E8%BF%9B%E5%88%B6%E5%BA%8F%E5%88%97%E5%8C%96%E5%B8%AE%E5%8A%A9%E7%B1%BB.html&quot;&gt;C# dotnet 一个看上去还能用的二进制序列化帮助类&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-WeakLazy-%E5%BC%B1%E5%BC%95%E7%94%A8%E7%9A%84%E5%BB%B6%E8%BF%9F%E5%88%9D%E5%A7%8B%E5%8C%96%E5%AE%9E%E7%8E%B0%E6%96%B9%E6%B3%95.html&quot;&gt;C# dotnet WeakLazy 弱引用的延迟初始化实现方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E9%AD%94%E6%B3%95%E4%B9%A6.html&quot;&gt;dotnet 动态代理魔法书&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E8%A7%A3%E6%9E%90-sln-%E6%96%87%E4%BB%B6.html&quot;&gt;C＃ 解析 sln 文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E7%AE%80%E5%8D%95%E5%AE%9E%E7%8E%B0-sln-%E5%92%8C-slnx-%E4%B8%A4%E4%B8%AA%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88%E6%96%87%E4%BB%B6%E6%A0%BC%E5%BC%8F%E4%BA%92%E8%BD%AC.html&quot;&gt;简单实现 sln 和 slnx 两个解决方案文件格式互转&lt;/a&gt;
&lt;!-- [简单实现 sln 和 slnx 两个解决方案文件格式互转 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18782265 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E9%87%91%E9%A2%9D%E8%BD%AC%E4%B8%AD%E6%96%87%E5%A4%A7%E5%86%99.html&quot;&gt;C# 金额转中文大写&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%B8%8D%E7%94%B3%E8%AF%B7%E9%A2%9D%E5%A4%96%E6%95%B0%E7%BB%84%E7%A9%BA%E9%97%B4%E5%90%88%E5%B9%B6%E5%A4%9A%E4%B8%AA%E5%8F%AA%E8%AF%BB%E6%95%B0%E7%BB%84%E5%88%97%E8%A1%A8.html&quot;&gt;dotnet 不申请额外数组空间合并多个只读数组列表&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8-C-%E7%88%AC%E8%99%AB%E8%8E%B7%E5%BE%97%E4%B8%93%E6%A0%8F%E5%8D%9A%E5%AE%A2%E6%9B%B4%E6%96%B0%E6%8E%92%E8%A1%8C.html&quot;&gt;如何使用 C# 爬虫获得专栏博客更新排行&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%9E%9A%E4%B8%BE%E5%BD%93%E5%89%8D%E8%AE%BE%E5%A4%87wifi%E7%83%AD%E7%82%B9.html&quot;&gt;dotnet 枚举当前设备wifi热点&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%AF%B9-DateTime-%E6%8E%92%E5%BA%8F.html&quot;&gt;dotnet 对 DateTime 排序&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%8E%B7%E5%8F%96%E6%9C%AC%E6%9C%BA-IP-%E5%9C%B0%E5%9D%80%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet 获取本机 IP 地址方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dot-net-double-%E6%95%B0%E7%BB%84%E8%BD%AC-float-%E6%95%B0%E7%BB%84.html&quot;&gt;dot net double 数组转 float 数组&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E4%BB%8E-short-%E8%BD%AC-byte-%E6%96%B9%E6%B3%95.html&quot;&gt;C# 从 short 转 byte 方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-16-%E8%BF%9B%E5%88%B6%E5%AD%97%E7%AC%A6%E4%B8%B2%E8%BD%AC-int.html&quot;&gt;C# 16 进制字符串转 int &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E5%AF%B9-byte-%E6%95%B0%E7%BB%84%E8%BF%9B%E8%A1%8C%E6%A8%A1%E5%BC%8F%E6%90%9C%E7%B4%A2.html&quot;&gt;C# 对 byte 数组进行模式搜索&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E5%BF%AB%E9%80%9F%E9%87%8A%E6%94%BE%E5%86%85%E5%AD%98%E7%9A%84%E5%A4%A7%E6%95%B0%E7%BB%84.html&quot;&gt;C# 快速释放内存的大数组&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E5%A4%8D%E5%88%B6%E5%88%97%E8%A1%A8.html&quot;&gt;C# 复制列表&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E5%A4%A7%E7%AB%AF%E5%B0%8F%E7%AB%AF%E8%BD%AC%E6%8D%A2.html&quot;&gt;C# 大端小端转换&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%9F%BA%E4%BA%8E-INotifyPropertyChanged-%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA-CLR-%E5%B1%9E%E6%80%A7%E7%BB%91%E5%AE%9A%E8%BE%85%E5%8A%A9%E7%B1%BB.html&quot;&gt;dotnet C# 基于 INotifyPropertyChanged 实现一个 CLR 属性绑定辅助类&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%AE%9E%E7%8E%B0-GetHashCode-%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet C# 实现 GetHashCode 的方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%88%B6%E4%BD%9C%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%9A%84%E8%87%AA%E5%8A%A8%E6%9B%B4%E6%96%B0%E7%B3%BB%E7%BB%9F%E6%97%A5%E6%9C%9F%E6%97%B6%E9%97%B4%E5%B7%A5%E5%85%B7.html&quot;&gt;dotnet 制作一个简单的自动更新系统日期时间工具&lt;/a&gt;
&lt;!-- [dotnet 制作一个简单的自动更新系统日期时间工具 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19344403 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%9C%A8%E6%96%B0%E8%BF%9B%E7%A8%8B%E6%89%A7%E8%A1%8C%E6%9F%90%E6%AE%B5%E5%A7%94%E6%89%98%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet 在新进程执行某段委托的方法&lt;/a&gt;
&lt;!-- [dotnet 在新进程执行某段委托的方法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19630685 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;已知问题&quot;&gt;已知问题&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AD%A6%E6%83%95-C-%E7%9A%84-is-var-%E5%86%99%E6%B3%95.html&quot;&gt;dotnet 警惕 C# 的 is var 写法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AD%A6%E6%83%95%E4%BD%BF%E7%94%A8-StackTrace-%E5%8A%A0%E8%8E%B7%E5%8F%96%E6%96%B9%E6%B3%95%E6%A0%87%E8%AE%B0-Attribute-%E7%89%B9%E6%80%A7%E5%9C%A8-Release-%E4%B8%8B%E8%A2%AB%E5%86%85%E8%81%94.html&quot;&gt;dotnet 警惕使用 StackTrace 加获取方法标记 Attribute 特性在 Release 下被内联&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%9C%A8%E6%9E%90%E6%9E%84%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8-ThreadLocal-%E4%B9%9F%E8%AE%B8%E4%BC%9A%E6%8A%9B%E5%87%BA%E5%AF%B9%E6%96%B9%E5%B7%B2%E9%87%8A%E6%94%BE.html&quot;&gt;dotnet 在析构函数调用 ThreadLocal 也许会抛出对方已释放&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-NamedPipeClientStream-%E8%BF%9E%E6%8E%A5%E4%B8%80%E4%B8%AA%E4%B8%8D%E5%AD%98%E5%9C%A8%E7%AE%A1%E9%81%93%E6%9C%8D%E5%8A%A1%E5%90%8D%E5%B0%86%E4%B8%8D%E6%96%AD%E7%A9%BA%E8%B7%91-CPU-%E8%B5%84%E6%BA%90.html&quot;&gt;dotnet 使用 NamedPipeClientStream 连接一个不存在管道服务名将不断空跑 CPU 资源&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E8%BF%9B%E7%A8%8B%E5%88%9B%E5%BB%BA%E5%A4%AA%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%B0%86%E4%BC%9A%E6%8A%9B%E5%87%BA-OutOfMemoryException-%E5%BC%82%E5%B8%B8.html&quot;&gt;dotnet C# 应用程序进程创建太多线程将会抛出 OutOfMemoryException 异常&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E4%BD%BF%E7%94%A8-Directory.EnumerateXXX-%E6%96%B9%E6%B3%95%E6%9E%9A%E4%B8%BE-C-%E7%9B%98%E6%A0%B9%E8%B7%AF%E5%BE%84%E5%8F%AF%E8%83%BD%E9%94%99%E8%AF%AF%E7%9A%84%E9%97%AE%E9%A2%98.html&quot;&gt;dotnet 已知问题 使用 Directory.EnumerateXXX 方法枚举 C 盘根路径可能错误的问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-6-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-ManualResetEventSlim-%E7%9A%84-Set-%E6%96%B9%E6%B3%95%E6%8A%9B%E5%87%BA%E7%A9%BA%E5%BC%82%E5%B8%B8.html&quot;&gt;dotnet 6 已知问题 ManualResetEventSlim 的 Set 方法抛出空异常&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AD%A6%E6%83%95-async-void-%E7%BA%BF%E7%A8%8B%E9%A1%B6%E5%B1%82%E5%BC%82%E5%B8%B8.html&quot;&gt;dotnet 警惕 async void 线程顶层异常&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-6-%E5%9C%A8-win7-%E7%B3%BB%E7%BB%9F-AES-CFB-%E6%8A%9B%E5%87%BA%E4%B8%8D%E6%94%AF%E6%8C%81%E5%BC%82%E5%B8%B8.html&quot;&gt;dotnet 6 在 win7 系统 AES CFB 抛出不支持异常&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E9%94%99%E8%AF%AF%E6%A0%87%E8%AE%B0-MethodImplOptions.InternalCall-%E7%89%B9%E6%80%A7%E5%8F%82%E6%95%B0%E5%B0%86%E4%BC%9A%E5%9C%A8%E7%B1%BB%E5%9E%8B%E8%AE%BF%E9%97%AE%E4%B9%8B%E5%89%8D%E6%8A%9B%E5%87%BA-TypeLoadException-%E5%BC%82%E5%B8%B8.html&quot;&gt;dotnet 已知问题 错误标记 MethodImplOptions.InternalCall 特性参数将会在类型访问之前抛出 TypeLoadException 异常&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E8%AD%A6%E6%83%95-StreamReader-%E7%9A%84-EndOfStream-%E5%8D%A1%E4%BD%8F%E7%BA%BF%E7%A8%8B.html&quot;&gt;dotnet 已知问题 警惕 StreamReader 的 EndOfStream 卡住线程&lt;/a&gt;
&lt;!-- [dotnet 已知问题 警惕 StreamReader 的 EndOfStream 卡住线程 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18397603 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AD%A6%E6%83%95%E8%AE%BE%E7%BD%AE-StreamReader-%E7%9A%84-BaseStream-%E7%9A%84-Position-%E5%B1%9E%E6%80%A7.html&quot;&gt;dotnet 警惕设置 StreamReader 的 BaseStream 的 Position 属性&lt;/a&gt;
&lt;!-- [dotnet 警惕设置 StreamReader 的 BaseStream 的 Position 属性 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18835047 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AE%B0-QEMU-%E8%AE%A9-dotnet-6-%E7%A8%8B%E5%BA%8F%E6%8A%9B%E5%87%BA%E7%A9%BA%E5%BC%82%E5%B8%B8%E9%97%AE%E9%A2%98.html&quot;&gt;记 QEMU 让 dotnet 6 程序抛出空异常问题&lt;/a&gt;
&lt;!-- [记 QEMU 让 dotnet 6 程序抛出空异常问题 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18863488 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-NamedPipeClientStream-%E8%BF%9E%E6%8E%A5%E4%B8%8D%E5%AD%98%E5%9C%A8%E7%9A%84%E6%9C%8D%E5%8A%A1%E5%9C%A8%E5%86%85%E9%83%A8%E6%8A%9B%E5%87%BA%E5%BC%82%E5%B8%B8.html&quot;&gt;dotnet 已知问题 NamedPipeClientStream 连接不存在的服务在内部抛出异常&lt;/a&gt;
&lt;!-- [dotnet 已知问题 NamedPipeClientStream 连接不存在的服务在内部抛出异常 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18990724 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;混淆&quot;&gt;混淆&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E5%88%A4%E6%96%AD%E6%96%87%E4%BB%B6%E6%98%AF%E5%90%A6%E8%A2%AB%E6%B7%B7%E6%B7%86.html&quot;&gt;C＃判断文件是否被混淆&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-6-%E4%BD%BF%E7%94%A8-Obfuscar-%E8%BF%9B%E8%A1%8C%E4%BB%A3%E7%A0%81%E6%B7%B7%E6%B7%86.html&quot;&gt;dotnet 6 使用 Obfuscar 进行代码混淆&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;wmi&quot;&gt;WMI&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%80%9A%E8%BF%87-WMI-%E8%8E%B7%E5%8F%96%E6%8C%87%E5%AE%9A%E8%BF%9B%E7%A8%8B%E7%9A%84%E8%BE%93%E5%85%A5%E5%91%BD%E4%BB%A4%E8%A1%8C.html&quot;&gt;dotnet 通过 WMI 获取指定进程的输入命令行&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%80%9A%E8%BF%87-WMI-%E8%8E%B7%E5%8F%96%E8%AE%BE%E5%A4%87%E5%8E%82%E5%95%86.html&quot;&gt;dotnet 通过 WMI 获取设备厂商&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%80%9A%E8%BF%87-WMI-%E8%8E%B7%E5%8F%96%E7%B3%BB%E7%BB%9F%E8%A1%A5%E4%B8%81.html&quot;&gt;dotnet 通过 WMI 获取系统补丁&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%80%9A%E8%BF%87-WMI-%E8%8E%B7%E5%8F%96%E7%B3%BB%E7%BB%9F%E5%AE%89%E8%A3%85%E7%9A%84%E9%A9%B1%E5%8A%A8.html&quot;&gt;dotnet 通过 WMI 获取系统安装的驱动&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%80%9A%E8%BF%87-WMI-%E8%8E%B7%E5%8F%96%E7%B3%BB%E7%BB%9F%E5%90%AF%E5%8A%A8%E7%9A%84%E6%9C%8D%E5%8A%A1.html&quot;&gt;dotnet 通过 WMI 获取系统启动的服务&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%80%9A%E8%BF%87-WMI-%E8%8E%B7%E5%8F%96%E7%B3%BB%E7%BB%9F%E4%BF%A1%E6%81%AF.html&quot;&gt;dotnet 通过 WMI 获取系统信息&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%80%9A%E8%BF%87-WMI-%E6%8B%BF%E5%88%B0%E6%98%BE%E5%8D%A1%E4%BF%A1%E6%81%AF.html&quot;&gt;dotnet 通过 WMI 拿到显卡信息&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%80%9A%E8%BF%87-WMI-%E8%8E%B7%E5%8F%96%E7%B3%BB%E7%BB%9F%E5%AE%89%E8%A3%85%E8%BD%AF%E4%BB%B6.html&quot;&gt;dotnet 通过 WMI 获取系统安装软件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/PowerShell-%E9%80%9A%E8%BF%87-WMI-%E8%8E%B7%E5%8F%96%E7%B3%BB%E7%BB%9F%E6%9C%8D%E5%8A%A1.html&quot;&gt;PowerShell 通过 WMI 获取系统服务&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/PowerShell-%E9%80%9A%E8%BF%87-WMI-%E8%8E%B7%E5%8F%96%E7%B3%BB%E7%BB%9F%E5%AE%89%E8%A3%85%E8%BD%AF%E4%BB%B6.html&quot;&gt;PowerShell 通过 WMI 获取系统安装软件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/PowerShell-%E9%80%9A%E8%BF%87-WMI-%E8%8E%B7%E5%8F%96%E7%B3%BB%E7%BB%9F%E4%BF%A1%E6%81%AF.html&quot;&gt;PowerShell 通过 WMI 获取系统信息&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/PowerShell-%E4%BD%BF%E7%94%A8-WMI-%E8%8E%B7%E5%8F%96%E4%BF%A1%E6%81%AF.html&quot;&gt;PowerShell 使用 WMI 获取信息&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/PowerShell-%E9%80%9A%E8%BF%87-WMI-%E8%8E%B7%E5%8F%96%E7%B3%BB%E7%BB%9F%E5%AE%89%E8%A3%85%E7%9A%84%E9%A9%B1%E5%8A%A8.html&quot;&gt;PowerShell 通过 WMI 获取系统安装的驱动&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/PowerShell-%E9%80%9A%E8%BF%87-WMI-%E8%8E%B7%E5%8F%96%E8%AE%BE%E5%A4%87%E5%8E%82%E5%95%86.html&quot;&gt;PowerShell 通过 WMI 获取设备厂商&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/PowerShell-%E9%80%9A%E8%BF%87-WMI-%E8%8E%B7%E5%8F%96%E8%A1%A5%E4%B8%81.html&quot;&gt;PowerShell 通过 WMI 获取补丁&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/PowerShell-%E6%8B%BF%E5%88%B0%E6%98%BE%E5%8D%A1%E4%BF%A1%E6%81%AF.html&quot;&gt;PowerShell 拿到显卡信息&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;跨平台开发&quot;&gt;跨平台开发&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-5-%E5%88%A4%E6%96%AD%E5%BD%93%E5%89%8D%E7%A8%8B%E5%BA%8F%E8%BF%90%E8%A1%8C%E5%9C%A8-Windows-%E7%B3%BB%E7%BB%9F-Linux-%E7%B3%BB%E7%BB%9F%E7%9A%84%E7%AE%80%E5%8D%95%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet 5 判断当前程序运行在 Windows 系统 Linux 系统的简单方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-core-%E5%AE%89%E8%A3%85%E5%9C%A8-Redhat6-RHEL-6-%E6%88%96-CentOS-6-%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet core 安装在 Redhat6 RHEL 6 或 CentOS 6 的方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%9C%A8-Linux-%E4%B8%8B%E7%9A%84-GDI-%E5%BA%93%E5%AF%B9-EMF-%E5%9B%BE%E7%89%87%E6%A0%BC%E5%BC%8F%E7%9A%84%E6%94%AF%E6%8C%81.html&quot;&gt;dotnet 在 Linux 下的 GDI 库对 EMF 图片格式的支持&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-XWT-%E6%9E%84%E5%BB%BA%E8%B7%A8%E5%B9%B3%E5%8F%B0%E5%AE%A2%E6%88%B7%E7%AB%AF-%E5%85%A5%E9%97%A8%E7%AF%87.html&quot;&gt;dotnet 使用 XWT 构建跨平台客户端 入门篇&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%B5%8B%E8%AF%95%E5%9C%A8-Linux-%E7%B3%BB%E7%BB%9F%E4%B8%8A%E7%9A%84-Environment.GetFolderPath-%E8%A1%8C%E4%B8%BA.html&quot;&gt;dotnet 测试在 Linux 系统上的 Environment.GetFolderPath 行为&lt;/a&gt;
&lt;!-- [dotnet 测试在 Linux 系统上的 Environment.GetFolderPath 行为 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/17970814 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%B5%8B%E8%AF%95%E5%9C%A8-UOS-Linux-%E4%B8%8A%E4%BD%BF%E7%94%A8-Process-Start-%E6%89%93%E5%BC%80%E6%96%87%E4%BB%B6%E7%9A%84%E8%A1%8C%E4%B8%BA.html&quot;&gt;dotnet 测试在 UOS Linux 上使用 Process Start 打开文件的行为&lt;/a&gt;
&lt;!-- [dotnet 测试在 UOS Linux 上使用 Process Start 打开文件的行为 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/17985683 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AE%B0%E5%9C%A8-Linux-%E4%B8%8A%E6%9F%90%E4%BA%9B%E6%96%87%E4%BB%B6%E7%9A%84%E6%96%87%E4%BB%B6%E9%95%BF%E5%BA%A6%E4%B8%BA-0-%E4%BD%86%E6%98%AF%E5%AD%98%E5%9C%A8%E5%86%85%E5%AE%B9.html&quot;&gt;dotnet 记在 Linux 上某些文件的文件长度为 0 但是存在内容&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%9C%A8-Linux-%E7%B3%BB%E7%BB%9F%E8%AE%BE%E7%BD%AE%E6%88%96%E8%8E%B7%E5%8F%96%E9%9F%B3%E9%87%8F%E4%BB%A5%E5%8F%8A%E5%8F%98%E6%9B%B4%E9%9F%B3%E9%87%8F%E6%94%B6%E5%88%B0%E9%80%9A%E7%9F%A5.html&quot;&gt;dotnet C# 在 Linux 系统设置或获取音量以及变更音量收到通知&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%A7%A3%E5%86%B3-Avalonia-%E5%9C%A8-OpenKylin-%E9%BA%92%E9%BA%9F%E7%B3%BB%E7%BB%9F%E8%BF%90%E8%A1%8C%E6%89%BE%E4%B8%8D%E5%88%B0%E9%BB%98%E8%AE%A4%E5%AD%97%E4%BD%93%E5%90%AF%E5%8A%A8%E5%A4%B1%E8%B4%A5.html&quot;&gt;dotnet 解决 Avalonia 在 OpenKylin 麒麟系统运行找不到默认字体启动失败&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/GtkSharp-%E8%8E%B7%E5%8F%96%E8%A7%A6%E6%91%B8%E5%AE%BD%E5%BA%A6%E9%AB%98%E5%BA%A6%E9%9D%A2%E7%A7%AF%E5%B0%BA%E5%AF%B8%E4%BF%A1%E6%81%AF.html&quot;&gt;GtkSharp 获取触摸宽度高度面积尺寸信息&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/GtkSharp-%E8%AE%BE%E7%BD%AE%E7%AA%97%E5%8F%A3%E8%83%8C%E6%99%AF%E9%80%8F%E6%98%8E.html&quot;&gt;GtkSharp 设置窗口背景透明&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BF%AE%E5%A4%8D-Debian-%E5%AE%89%E8%A3%85-dotnet-%E5%A4%B1%E8%B4%A5-depends-on-ca-certificates.html&quot;&gt;修复 Debian 安装 dotnet 失败 depends on ca-certificates&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E5%A6%82%E4%BD%95-SSH-%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%95-Linux-%E7%9A%84-dotnet-%E5%BA%94%E7%94%A8%E7%9A%84%E5%90%AF%E5%8A%A8.html&quot;&gt;VisualStudio 如何 SSH 远程调试 Linux 的 dotnet 应用的启动&lt;/a&gt;
&lt;!-- [VisualStudio 如何 SSH 远程调试 Linux 的 dotnet 应用的启动 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18238164 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Avalonia-%E5%9C%A8-X11-%E4%B8%8B%E4%BD%BF%E7%94%A8%E8%BD%AF%E6%B8%B2%E6%9F%93%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;Avalonia 在 X11 下使用软渲染的方法&lt;/a&gt;
&lt;!-- [Avalonia 在 X11 下使用软渲染的方法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18317439 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%88%B6%E4%BD%9C%E4%B8%80%E4%B8%AA%E9%BE%99%E8%8A%AF%E6%97%A7%E4%B8%96%E7%95%8C%E7%9A%84-dotnet-sdk-docker-%E9%95%9C%E5%83%8F.html&quot;&gt;制作一个龙芯旧世界的 dotnet sdk docker 镜像&lt;/a&gt;
&lt;!-- [制作一个龙芯旧世界的 dotnet sdk docker 镜像 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18521578 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-8-%E7%89%88%E6%9C%AC%E4%B8%8E%E9%93%B6%E6%B2%B3%E9%BA%92%E9%BA%9FV10%E5%92%8CUOS%E7%B3%BB%E7%BB%9F%E7%9A%84-glibc-%E5%85%BC%E5%AE%B9%E6%80%A7.html&quot;&gt;dotnet 8 版本与银河麒麟V10和UOS系统的 glibc 兼容性&lt;/a&gt;
&lt;!-- [dotnet 8 版本与银河麒麟V10和UOS系统的 glibc 兼容性 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18161216 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AE%B0-dotnet-campus-%E7%BB%84%E7%BB%87%E4%B8%BA%E9%80%82%E9%85%8D%E9%BE%99%E8%8A%AF%E6%89%80%E5%81%9A%E7%9A%84%E6%9B%B4%E6%94%B9.html&quot;&gt;记 dotnet campus 组织为适配龙芯所做的更改&lt;/a&gt;
&lt;!-- [记 dotnet campus 组织为适配龙芯所做的更改 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18542775 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%88%A4%E6%96%AD%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E5%9C%A8-Wine-%E9%87%8C%E8%BF%90%E8%A1%8C%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet C# 判断应用程序在 Wine 里运行的方法&lt;/a&gt;
&lt;!-- [dotnet C# 判断应用程序在 Wine 里运行的方法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18808709 ) --&gt;&lt;/p&gt;

&lt;h4 id=&quot;uos&quot;&gt;UOS&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%9C%A8-UOS-%E5%9B%BD%E4%BA%A7%E7%B3%BB%E7%BB%9F%E4%B8%8A%E5%AE%89%E8%A3%85-dotnet-sdk-%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet 在 UOS 国产系统上安装 dotnet sdk 的方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/UOS-%E5%BC%80%E5%90%AF-VisualStudio-%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%95-.NET-%E5%BA%94%E7%94%A8%E4%B9%8B%E6%97%85.html&quot;&gt;UOS 开启 VisualStudio 远程调试 .NET 应用之旅&lt;/a&gt;
&lt;!-- [UOS 开启 VisualStudio 远程调试 .NET 应用之旅 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18086533 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E5%A6%82%E4%BD%95-SSH-%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%95-Linux-%E7%9A%84-dotnet-%E5%BA%94%E7%94%A8%E7%9A%84%E5%90%AF%E5%8A%A8.html&quot;&gt;VisualStudio 如何 SSH 远程调试 Linux 的 dotnet 应用的启动&lt;/a&gt;
&lt;!-- [VisualStudio 如何 SSH 远程调试 Linux 的 dotnet 应用的启动 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18238164 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/fireicesion/p/15134245.html&quot;&gt;统信UOS系统部署.Net Core 5.0 - 火冰·瓶 - 博客园&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/yujing2013/article/details/121741789&quot;&gt;Uos NetCoreSdk环境部署_uos生态适配的博客-CSDN博客_netcore部署环境&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%9C%A8-UOS-%E5%9B%BD%E4%BA%A7%E7%B3%BB%E7%BB%9F%E4%B8%8A%E5%AE%89%E8%A3%85-Mono-%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet 在 UOS 国产系统上安装 Mono 开发工具的方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%9C%A8-UOS-%E5%9B%BD%E4%BA%A7%E7%B3%BB%E7%BB%9F%E4%B8%8A%E5%AE%89%E8%A3%85-MonoDevelop-%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7.html&quot;&gt;dotnet 在 UOS 国产系统上安装 MonoDevelop 开发工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%9C%A8-UOS-%E5%9B%BD%E4%BA%A7%E7%B3%BB%E7%BB%9F%E4%B8%8A%E4%BD%BF%E7%94%A8-MonoDevelop-%E8%BF%9B%E8%A1%8C%E6%8B%96%E6%8E%A7%E4%BB%B6%E5%BC%80%E5%8F%91-GTK-%E5%BA%94%E7%94%A8.html&quot;&gt;dotnet 在 UOS 国产系统上使用 MonoDevelop 进行拖控件开发 GTK 应用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%9C%A8-UOS-%E5%9B%BD%E4%BA%A7%E7%B3%BB%E7%BB%9F%E4%B8%8A%E4%BD%BF%E7%94%A8-MonoDevelop-%E5%88%9B%E5%BB%BA-GTK-%E5%85%A8%E5%B9%B3%E5%8F%B0%E5%B8%A6%E7%95%8C%E9%9D%A2%E5%BA%94%E7%94%A8.html&quot;&gt;dotnet 在 UOS 国产系统上使用 MonoDevelop 创建 GTK 全平台带界面应用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%9C%A8-UOS-%E5%9B%BD%E4%BA%A7%E7%B3%BB%E7%BB%9F%E4%B8%8A%E4%BD%BF%E7%94%A8-Xamarin-Forms-%E5%88%9B%E5%BB%BA-xaml-%E7%95%8C%E9%9D%A2%E7%9A%84-GTK-%E5%BA%94%E7%94%A8.html&quot;&gt;dotnet 在 UOS 国产系统上使用 Xamarin Forms 创建 xaml 界面的 GTK 应用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-Avalonia-%E5%BC%80%E5%8F%91-UOS-%E5%8E%9F%E7%94%9F%E5%BA%94%E7%94%A8.html&quot;&gt;dotnet 使用 Avalonia 开发 UOS 原生应用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%9C%A8%E5%9B%BD%E4%BA%A7-UOS-%E7%B3%BB%E7%BB%9F%E5%88%A9%E7%94%A8-dotnet-tool-%E5%B7%A5%E5%85%B7%E5%81%9A%E6%96%87%E4%BB%B6%E4%BC%A0%E8%BE%93.html&quot;&gt;dotnet 在国产 UOS 系统利用 dotnet tool 工具做文件传输&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%9C%A8-UOS-%E7%BB%9F%E4%BF%A1%E8%BF%90%E8%A1%8C-dotnet-%E7%A8%8B%E5%BA%8F%E6%8F%90%E7%A4%BA%E6%B2%A1%E6%9C%89%E9%80%9A%E8%BF%87%E7%B3%BB%E7%BB%9F%E5%AE%89%E5%85%A8%E9%AA%8C%E8%AF%81%E6%97%A0%E6%B3%95%E8%BF%90%E8%A1%8C.html&quot;&gt;在 UOS 统信运行 dotnet 程序提示没有通过系统安全验证无法运行&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%9C%A8-UOS-%E7%BB%9F%E4%BF%A1%E5%AE%89%E8%A3%85-dotnet-sdk-%E5%A4%B1%E8%B4%A5-%E6%8F%90%E7%A4%BA-failed-the-verification.html&quot;&gt;在 UOS 统信安装 dotnet sdk 失败 提示 failed the verification&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%9C%A8-UOS-%E7%BB%9F%E4%BF%A1%E7%B3%BB%E7%BB%9F%E4%B8%8A%E8%BF%90%E8%A1%8C-UNO-%E7%A8%8B%E5%BA%8F%E8%BE%93%E5%85%A5%E6%97%B6%E9%97%AA%E7%83%81%E9%BB%91%E5%B1%8F%E9%97%AE%E9%A2%98.html&quot;&gt;dotnet 在 UOS 统信系统上运行 UNO 程序输入时闪烁黑屏问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E7%BB%9F%E4%BF%A1-UOS-%E8%BF%90%E8%A1%8C-UNO-FrameBuffer-%E5%BA%94%E7%94%A8%E9%94%99%E8%AF%AF-Failed-to-open-FrameBuffer-device.html&quot;&gt;dotnet 统信 UOS 运行 UNO FrameBuffer 应用错误 Failed to open FrameBuffer device&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%B8%80%E6%AD%A5%E6%AD%A5%E6%95%99%E4%BD%A0%E5%9C%A8-Windows-%E4%B8%8A%E6%9E%84%E5%BB%BA-dotnet-%E7%B3%BB%E5%BA%94%E7%94%A8%E7%9A%84-UOS-%E8%BD%AF%E4%BB%B6%E5%AE%89%E8%A3%85%E5%8C%85.html&quot;&gt;一步步教你在 Windows 上构建 dotnet 系应用的 UOS 软件安装包&lt;/a&gt;
&lt;!-- [一步步教你在 Windows 上构建 dotnet 系应用的 UOS 软件安装包 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/17925826.html ) --&gt;
&lt;!-- [一步步教你在 Windows 上构建 dotnet 系应用的 UOS 软件安装包-腾讯云开发者社区-腾讯云](https://cloud.tencent.com/developer/article/2374481 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Packaging.DebUOS-%E4%B8%93%E9%97%A8%E4%B8%BA-dotnet-%E5%BA%94%E7%94%A8%E5%88%B6%E4%BD%9C-UOS-%E5%AE%89%E8%A3%85%E5%8C%85.html&quot;&gt;Packaging.DebUOS 专门为 dotnet 应用制作 UOS 安装包&lt;/a&gt;
&lt;!-- [Packaging.DebUOS 专门为 dotnet 应用制作 UOS 安装包 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/17995729 ) --&gt;&lt;/p&gt;

&lt;h4 id=&quot;麒麟&quot;&gt;麒麟&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%BA%92%E9%BA%9F-Kylin-%E7%9A%84-X11-%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91%E8%AE%B0%E5%BD%95.html&quot;&gt;dotnet 麒麟 Kylin 的 X11 应用开发记录&lt;/a&gt;
&lt;!-- [dotnet 麒麟 Kylin 的 X11 应用开发记录 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18569239 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AE%B0-Kylin-%E9%BA%92%E9%BA%9F%E7%B3%BB%E7%BB%9F%E5%AE%89%E5%85%A8%E4%B8%AD%E5%BF%83%E6%8B%A6%E6%88%AA%E5%AF%BC%E8%87%B4-dotnet-sdk-%E6%89%BE%E4%B8%8D%E5%88%B0-OpenSsl-%E6%9E%84%E5%BB%BA%E5%A4%B1%E8%B4%A5.html&quot;&gt;记 Kylin 麒麟系统安全中心拦截导致 dotnet sdk 找不到 OpenSsl 构建失败&lt;/a&gt;
&lt;!-- [记 Kylin 麒麟系统安全中心拦截导致 dotnet sdk 找不到 OpenSsl 构建失败 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18514833 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AE%B0%E9%BE%99%E8%8A%AF%E9%BA%92%E9%BA%9F%E6%95%99%E8%82%B2%E7%89%88%E5%AE%89%E5%85%A8%E4%B8%AD%E5%BF%83%E6%8B%A6%E6%88%AA%E6%96%87%E4%BB%B6-%E5%AF%BC%E8%87%B4-docker-%E5%86%85-CI-CD-%E6%9E%84%E5%BB%BA%E5%A4%B1%E8%B4%A5.html&quot;&gt;dotnet 记龙芯麒麟教育版安全中心拦截文件 导致 docker 内 CI CD 构建失败&lt;/a&gt;
&lt;!-- [dotnet 记龙芯麒麟教育版安全中心拦截文件 导致 docker 内 CI CD 构建失败 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18545167 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AF%BB%E5%8F%96%E9%BA%92%E9%BA%9F%E7%B3%BB%E7%BB%9F%E7%9A%84%E5%90%84%E9%A1%B9%E7%89%88%E6%9C%AC%E4%BF%A1%E6%81%AF.html&quot;&gt;读取麒麟系统的各项版本信息&lt;/a&gt;
&lt;!-- [读取麒麟系统的各项版本信息 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18527091 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%A7%A3%E5%86%B3%E9%BA%92%E9%BA%9F-Kylin-%E7%B3%BB%E7%BB%9F%E6%8F%90%E7%A4%BA-IP-%E5%86%B2%E7%AA%81-%E6%97%A0%E6%B3%95%E6%AD%A3%E5%B8%B8%E8%BF%9E%E6%8E%A5%E5%88%B0%E7%BD%91%E7%BB%9C.html&quot;&gt;解决麒麟 Kylin 系统提示 IP 冲突 无法正常连接到网络&lt;/a&gt;
&lt;!-- [解决麒麟 Kylin 系统提示 IP 冲突 无法正常连接到网络 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18827933 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/linux-%E9%BA%92%E9%BA%9F%E7%B3%BB%E7%BB%9F%E4%BF%AE%E5%A4%8D-wmf2gd-%E8%BD%AC%E6%8D%A2-wmf-%E5%9B%BE%E7%89%87%E6%8F%90%E7%A4%BA-wmf_ipa_font_map-%E9%94%99%E8%AF%AF.html&quot;&gt;linux 麒麟系统修复 wmf2gd 转换 wmf 图片提示 wmf_ipa_font_map 错误&lt;/a&gt;
&lt;!-- [linux 麒麟系统修复 wmf2gd 转换 wmf 图片提示 wmf_ipa_font_map 错误 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19003836 ) --&gt;&lt;/p&gt;

&lt;h4 id=&quot;uno&quot;&gt;UNO&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/UNO-UnoConf-2020-%E5%9C%A8%E7%BA%BF%E4%BC%9A%E8%AE%AE-%E4%BA%86%E8%A7%A3%E5%85%A8%E5%B9%B3%E5%8F%B0%E6%96%B0%E5%BC%80%E5%8F%91%E6%A1%86%E6%9E%B6-%E9%9B%B6%E8%B7%9D%E7%A6%BB%E6%8E%A5%E8%A7%A6%E5%AE%98%E6%96%B9%E5%BC%80%E5%8F%91%E8%80%85.html&quot;&gt;UNO UnoConf 2020 在线会议 了解全平台新开发框架 零距离接触官方开发者&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BB%8E-WPF-%E6%90%AC%E8%BF%81%E5%88%B0-UOS-%E4%B8%8B%E7%9A%84-UNO-%E7%9A%84%E7%AC%94%E8%AE%B0.html&quot;&gt;从 WPF 搬迁到 UOS 下的 UNO 的笔记&lt;/a&gt; &lt;a href=&quot;https://cloud.tencent.com/developer/article/2364682&quot;&gt;腾讯云&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BD%BF%E7%94%A8-Uno-Islands-%E5%9C%A8%E7%8E%B0%E6%9C%89-WPF-%E9%87%8C%E9%9D%A2%E5%B5%8C%E5%85%A5-Uno-%E6%A1%86%E6%9E%B6.html&quot;&gt;使用 Uno Islands 在现有 WPF 里面嵌入 Uno 框架&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-UNO-%E6%B5%8B%E8%AF%95%E5%9B%BA%E5%AE%9A%E5%B0%BA%E5%AF%B8%E4%B8%94%E6%B0%B4%E5%B9%B3%E5%92%8C%E5%9E%82%E7%9B%B4%E5%AF%B9%E9%BD%90%E8%AE%BE%E7%BD%AE-Stretch-%E7%9A%84%E5%85%83%E7%B4%A0%E5%9C%A8%E5%AE%B9%E5%99%A8%E5%86%85%E7%9A%84%E5%B8%83%E5%B1%80%E8%A1%8C%E4%B8%BA.html&quot;&gt;UNO 测试固定尺寸且水平和垂直对齐设置 Stretch 的元素在容器内的布局行为&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%A6%82%E4%BD%95%E5%B0%86-Microsoft.Maui.Graphics-%E5%AF%B9%E6%8E%A5%E5%88%B0-UNO-%E6%A1%86%E6%9E%B6.html&quot;&gt;dotnet 如何将 Microsoft.Maui.Graphics 对接到 UNO 框架&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BF%AE%E5%A4%8D-Uno-%E4%B8%AD%E6%96%87%E4%B9%B1%E7%A0%81.html&quot;&gt;dotnet 修复 Uno 中文乱码&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%9C%A8-UOS-%E7%BB%9F%E4%BF%A1%E7%B3%BB%E7%BB%9F%E4%B8%8A%E8%BF%90%E8%A1%8C-UNO-%E7%A8%8B%E5%BA%8F%E8%BE%93%E5%85%A5%E6%97%B6%E9%97%AA%E7%83%81%E9%BB%91%E5%B1%8F%E9%97%AE%E9%A2%98.html&quot;&gt;dotnet 在 UOS 统信系统上运行 UNO 程序输入时闪烁黑屏问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E7%BB%9F%E4%BF%A1-UOS-%E8%BF%90%E8%A1%8C-UNO-FrameBuffer-%E5%BA%94%E7%94%A8%E9%94%99%E8%AF%AF-Failed-to-open-FrameBuffer-device.html&quot;&gt;dotnet 统信 UOS 运行 UNO FrameBuffer 应用错误 Failed to open FrameBuffer device&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-UNO-%E5%A6%82%E4%BD%95%E5%9C%A8%E8%B0%83%E8%AF%95%E4%B8%8B%E8%BE%93%E5%87%BA%E7%95%8C%E9%9D%A2%E5%B1%82%E7%BA%A7%E7%BB%93%E6%9E%84.html&quot;&gt;dotnet UNO 如何在调试下输出界面层级结构&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/UNO.Skia.Gtk-%E8%AE%BE%E7%BD%AE%E7%AA%97%E5%8F%A3%E5%B0%BA%E5%AF%B8%E5%8F%98%E5%8C%96%E6%96%B9%E6%B3%95.html&quot;&gt;UNO.Skia.Gtk 设置窗口尺寸变化方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/UNO-%E8%AE%BE%E7%BD%AE%E5%B9%B3%E5%8F%B0%E8%BF%9B%E5%85%A5%E5%85%A8%E5%B1%8F%E7%AA%97%E5%8F%A3%E6%A8%A1%E5%BC%8F%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;UNO 设置平台进入全屏窗口模式的方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BB%8E-Uno-Platform-4-%E6%9B%B4%E6%96%B0-Uno-Platform-5-%E7%9A%84%E8%BF%81%E7%A7%BB%E6%96%B9%E6%B3%95.html&quot;&gt;从 Uno Platform 4 更新 Uno Platform 5 的迁移方法&lt;/a&gt; &lt;a href=&quot;https://cloud.tencent.com/developer/article/2380234&quot;&gt;腾讯云&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/UNO-%E6%96%B0%E5%BB%BA%E5%9F%BA%E7%A1%80%E5%BA%93%E9%A1%B9%E7%9B%AE%E6%9E%84%E5%BB%BA%E6%8F%90%E7%A4%BA-UNOB0002-%E9%94%99%E8%AF%AF.html&quot;&gt;UNO 新建基础库项目构建提示 UNOB0002 错误&lt;/a&gt; &lt;a href=&quot;https://cloud.tencent.com/developer/article/2380233&quot;&gt;腾讯云&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/UNO-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E5%9C%A8%E5%90%8E%E5%8F%B0%E7%BA%BF%E7%A8%8B%E8%A7%A6%E5%8F%91-SKXamlCanvas-%E7%9A%84-Invalidate-%E4%B8%94%E5%9C%A8-PaintSurface-%E4%BA%8B%E4%BB%B6%E6%8A%9B%E5%87%BA%E5%BC%82%E5%B8%B8%E5%B0%86%E7%82%B8%E6%8E%89%E5%BA%94%E7%94%A8.html&quot;&gt;UNO 已知问题 在后台线程触发 SKXamlCanvas 的 Invalidate 且在 PaintSurface 事件抛出异常将炸掉应用&lt;/a&gt; &lt;a href=&quot;https://cloud.tencent.com/developer/article/2381479&quot;&gt;腾讯云&lt;/a&gt;
&lt;!-- [UNO 已知问题 在后台线程触发 SKXamlCanvas 的 Invalidate 且在 PaintSurface 事件抛出异常将炸掉应用 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/17975446 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/UNO-WinUI-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E5%9C%A8-XAML-%E6%9D%A1%E4%BB%B6%E6%9E%84%E5%BB%BA%E9%87%8C%E5%B0%86-win-%E5%B9%B3%E5%8F%B0%E5%8A%A0%E5%85%A5-Ignorable-%E5%B0%86%E6%9E%84%E5%BB%BA%E5%A4%B1%E8%B4%A5.html&quot;&gt;UNO WinUI 已知问题 在 XAML 条件构建里将 win 平台加入 Ignorable 将构建失败&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%9C%A8-UNO-%E9%87%8C%E8%8E%B7%E5%8F%96-X11-%E7%AA%97%E5%8F%A3%E6%8C%87%E9%92%88%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet 在 UNO 里获取 X11 窗口指针的方法&lt;/a&gt;
&lt;!-- [dotnet 在 UNO 里获取 X11 窗口指针的方法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18207530 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%A6%82%E4%BD%95%E8%AE%BF%E9%97%AE%E5%88%B0-UNO-%E6%A1%86%E6%9E%B6%E9%87%8C%E9%9D%A2%E7%9A%84-internal-%E4%B8%8D%E5%85%AC%E5%BC%80%E6%88%90%E5%91%98.html&quot;&gt;dotnet 如何访问到 UNO 框架里面的 internal 不公开成员&lt;/a&gt;
&lt;!-- [dotnet 如何访问到 UNO 框架里面的 internal 不公开成员 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18243188 ) --&gt;
&lt;!-- [Accessing Internal Members in the UNO Framework](/post/Accessing-Internal-Members-in-the-UNO-Framework.html ) --&gt;
&lt;!-- [Accessing Internal Members in the UNO Framework - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18243187 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%9E%8D%E5%90%88-Avalonia-%E5%92%8C-UNO-%E6%A1%86%E6%9E%B6.html&quot;&gt;dotnet 融合 Avalonia 和 UNO 框架&lt;/a&gt;
&lt;!-- [dotnet 融合 Avalonia 和 UNO 框架 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18263041 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%A7%A3%E5%86%B3-UNO-%E5%9C%A8-OpenKylin-%E9%BA%92%E9%BA%9F%E7%B3%BB%E7%BB%9F%E8%BF%90%E8%A1%8C%E6%89%BE%E4%B8%8D%E5%88%B0%E9%BB%98%E8%AE%A4%E5%AD%97%E4%BD%93%E5%90%AF%E5%8A%A8%E5%A4%B1%E8%B4%A5.html&quot;&gt;dotnet 解决 UNO 在 OpenKylin 麒麟系统运行找不到默认字体启动失败&lt;/a&gt;
&lt;!-- [dotnet 解决 UNO 在 OpenKylin 麒麟系统运行找不到默认字体启动失败 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18268131 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E9%80%9A%E8%BF%87%E6%97%A5%E5%BF%97%E5%88%A4%E6%96%AD-Uno-Platform-%E6%98%AF%E5%90%A6%E5%9C%A8-X11-%E4%BD%BF%E7%94%A8-OpenGL-%E6%B8%B2%E6%9F%93%E5%8A%A0%E9%80%9F%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;通过日志判断 Uno Platform 是否在 X11 使用 OpenGL 渲染加速的方法&lt;/a&gt;
&lt;!-- [通过日志判断 Uno Platform 是否在 X11 使用 OpenGL 渲染加速的方法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18319977 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E7%AE%80%E5%8D%95%E5%9C%A8-WinUI-%E4%BB%BF%E9%80%A0-WPF-%E7%9A%84-ColumnDefinition-SharedSizeGroup-%E5%85%B1%E4%BA%AB%E5%88%97%E5%AE%BD%E5%8A%9F%E8%83%BD.html&quot;&gt;简单在 WinUI 仿造 WPF 的 ColumnDefinition SharedSizeGroup 共享列宽功能&lt;/a&gt;
&lt;!-- [简单在 WinUI 仿造 WPF 的 ColumnDefinition SharedSizeGroup 共享列宽功能 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18353046 ) --&gt;&lt;/p&gt;

&lt;h4 id=&quot;avalonia&quot;&gt;Avalonia&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%9E%8D%E5%90%88-Avalonia-%E5%92%8C-UNO-%E6%A1%86%E6%9E%B6.html&quot;&gt;dotnet 融合 Avalonia 和 UNO 框架&lt;/a&gt;
&lt;!-- [dotnet 融合 Avalonia 和 UNO 框架 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18263041 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-Avalonia-%E5%BC%80%E5%8F%91-UOS-%E5%8E%9F%E7%94%9F%E5%BA%94%E7%94%A8.html&quot;&gt;dotnet 使用 Avalonia 开发 UOS 原生应用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%A7%A3%E5%86%B3-Avalonia-%E5%9C%A8-OpenKylin-%E9%BA%92%E9%BA%9F%E7%B3%BB%E7%BB%9F%E8%BF%90%E8%A1%8C%E6%89%BE%E4%B8%8D%E5%88%B0%E9%BB%98%E8%AE%A4%E5%AD%97%E4%BD%93%E5%90%AF%E5%8A%A8%E5%A4%B1%E8%B4%A5.html&quot;&gt;dotnet 解决 Avalonia 在 OpenKylin 麒麟系统运行找不到默认字体启动失败&lt;/a&gt;
&lt;!-- [dotnet 解决 Avalonia 在 OpenKylin 麒麟系统运行找不到默认字体启动失败 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18132455 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%AD%A6%E4%B9%A0-Avalonia-%E6%A1%86%E6%9E%B6%E7%AC%94%E8%AE%B0-%E5%A6%82%E4%BD%95%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA%E5%85%A8%E5%B1%8F%E7%BD%AE%E9%A1%B6%E7%9A%84-X11-%E5%BA%94%E7%94%A8%E7%AA%97%E5%8F%A3.html&quot;&gt;学习 Avalonia 框架笔记 如何创建一个全屏置顶的 X11 应用窗口&lt;/a&gt; &lt;a href=&quot;https://cloud.tencent.com/developer/article/2407503&quot;&gt;腾讯云&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%AD%A6%E4%B9%A0-Avalonia-%E6%A1%86%E6%9E%B6%E7%AC%94%E8%AE%B0-%E8%AE%BE%E7%BD%AE-X11-%E7%AA%97%E5%8F%A3%E4%BB%8E%E6%9C%80%E5%B0%8F%E5%8C%96%E7%8A%B6%E6%80%81%E8%BF%98%E5%8E%9F%E4%B8%BA%E6%AD%A3%E5%B8%B8%E7%8A%B6%E6%80%81.html&quot;&gt;学习 Avalonia 框架笔记 设置 X11 窗口从最小化状态还原为正常状态&lt;/a&gt;
&lt;!-- [学习 Avalonia 框架笔记 设置 X11 窗口从最小化状态还原为正常状态 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18197087 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Avalonia-11.1-%E8%8E%B7%E5%8F%96%E5%B9%B3%E5%8F%B0%E8%B0%83%E7%94%A8%E7%9A%84%E7%AA%97%E5%8F%A3%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;Avalonia 11.1 获取平台调用的窗口的方法&lt;/a&gt;
&lt;!-- [Avalonia 11.1 获取平台调用的窗口的方法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18350133 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Avalonia-%E5%90%8E%E5%8F%B0%E4%BB%A3%E7%A0%81%E7%AE%80%E5%8D%95%E6%92%AD%E6%94%BE%E5%8A%A8%E7%94%BB%E7%A4%BA%E4%BE%8B.html&quot;&gt;Avalonia 后台代码简单播放动画示例&lt;/a&gt;
&lt;!-- [Avalonia 后台代码简单播放动画示例 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18368582 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Avalonia-%E7%AE%80%E5%8D%95%E5%AE%9E%E7%8E%B0%E8%BE%93%E5%85%A5%E6%B3%95%E5%85%89%E6%A0%87%E8%B7%9F%E9%9A%8F%E6%95%88%E6%9E%9C.html&quot;&gt;Avalonia 简单实现输入法光标跟随效果&lt;/a&gt;
&lt;!-- [Avalonia 简单实现输入法光标跟随效果 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18669800 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Avalonia-%E5%B0%86-DrawingImage-%E4%BF%9D%E5%AD%98%E5%88%B0%E6%9C%AC%E5%9C%B0%E6%96%87%E4%BB%B6%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;Avalonia 将 DrawingImage 保存到本地文件的方法&lt;/a&gt;
&lt;!-- [Avalonia 将 DrawingImage 保存到本地文件的方法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18832016 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Avalonia-%E5%88%B6%E4%BD%9C-AOT-%E5%8D%95%E6%96%87%E4%BB%B6.html&quot;&gt;Avalonia 制作 AOT 单文件&lt;/a&gt;
&lt;!-- [Avalonia 制作 AOT 单文件 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19038846 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Avalonia-%E5%AE%9E%E7%8E%B0%E7%A6%BB%E5%B1%8F%E6%B8%B2%E6%9F%93%E8%83%BD%E5%8A%9B.html&quot;&gt;Avalonia 实现离屏渲染能力&lt;/a&gt;
&lt;!-- [Avalonia 实现离屏渲染能力 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19779219 ) --&gt;&lt;/p&gt;

&lt;h5 id=&quot;avalonia-界面效果&quot;&gt;Avalonia 界面效果&lt;/h5&gt;

&lt;p&gt;&lt;a href=&quot;/post/Avalonia-%E7%95%8C%E9%9D%A2%E6%95%88%E6%9E%9C-%E6%BB%9A%E5%8A%A8%E7%9A%84%E6%B8%90%E5%8F%98%E7%9F%A9%E5%BD%A2%E8%BE%B9%E6%A1%86.html&quot;&gt;Avalonia 界面效果 滚动的渐变矩形边框&lt;/a&gt;
&lt;!-- [Avalonia 界面效果 滚动的渐变矩形边框 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18803386 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Avalonia-%E7%95%8C%E9%9D%A2%E6%95%88%E6%9E%9C-%E4%B8%89%E4%B8%AA%E5%9C%86%E5%AE%9E%E7%8E%B0%E6%A8%A1%E7%B3%8A%E7%95%8C%E9%9D%A2%E5%8A%A8%E6%95%88%E8%83%8C%E6%99%AF.html&quot;&gt;Avalonia 界面效果 三个圆实现模糊界面动效背景&lt;/a&gt;
&lt;!-- [Avalonia 界面效果 三个圆实现模糊界面动效背景 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18803385 ) --&gt;&lt;/p&gt;

&lt;h5 id=&quot;avalonia-渲染&quot;&gt;Avalonia 渲染&lt;/h5&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%AF%B9%E6%AF%94-Avalonia-%E5%92%8C-WPF-%E7%9A%84%E6%B8%B2%E6%9F%93%E5%BB%B6%E8%BF%9F.html&quot;&gt;对比 Avalonia 和 WPF 的渲染延迟&lt;/a&gt;
&lt;!-- [对比 Avalonia 和 WPF 的渲染延迟 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19582321 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Avalonia-%E7%AE%80%E6%98%93%E5%AF%B9%E6%AF%94%E4%B8%8D%E5%90%8C%E7%9A%84-Win32CompositionMode-%E7%9A%84%E6%80%A7%E8%83%BD%E6%83%85%E5%86%B5.html&quot;&gt;Avalonia 简易对比不同的 Win32CompositionMode 的性能情况&lt;/a&gt;
&lt;!-- [Avalonia 简易对比不同的 Win32CompositionMode 的性能情况 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19582320 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Avalonia-%E7%AC%94%E8%BF%B9%E6%B8%B2%E6%9F%93%E5%A4%AA%E6%85%A2%E4%BA%86-%E7%94%A8-WPF-%E5%81%9A%E5%8A%A0%E9%80%9F%E5%B1%82.html&quot;&gt;Avalonia 笔迹渲染太慢了 用 WPF 做加速层&lt;/a&gt;
&lt;!-- [Avalonia 笔迹渲染太慢了 用 WPF 做加速层 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18835048 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Avalonia-%E5%9C%A8-X11-%E4%B8%8B%E4%BD%BF%E7%94%A8%E8%BD%AF%E6%B8%B2%E6%9F%93%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;Avalonia 在 X11 下使用软渲染的方法&lt;/a&gt;
&lt;!-- [Avalonia 在 X11 下使用软渲染的方法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18317439 ) --&gt;&lt;/p&gt;

&lt;h5 id=&quot;avalonia-已知问题&quot;&gt;Avalonia 已知问题&lt;/h5&gt;

&lt;p&gt;&lt;a href=&quot;/post/Avalonia-11.1-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-IterationCount-%E4%B8%BA-Infinite-%E7%9A%84%E5%8A%A8%E7%94%BB%E6%92%AD%E6%94%BE%E5%87%BA%E7%8E%B0%E5%BC%82%E5%B8%B8.html&quot;&gt;Avalonia 11.1 已知问题 IterationCount 为 Infinite 的动画播放出现异常&lt;/a&gt;
&lt;!-- [Avalonia 11.1 已知问题 IterationCount 为 Infinite 的动画播放出现异常 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18375091 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Avalonia-11.1-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E5%BA%94%E7%94%A8%E5%90%AF%E5%8A%A8%E6%97%B6-PointToScreen-%E6%97%A0%E6%B3%95%E8%8E%B7%E5%8F%96%E6%AD%A3%E7%A1%AE%E5%9D%90%E6%A0%87.html&quot;&gt;Avalonia 11.1 已知问题 应用启动时 PointToScreen 无法获取正确坐标&lt;/a&gt;
&lt;!-- [Avalonia 11.1 已知问题 应用启动时 PointToScreen 无法获取正确坐标 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18351896 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Avalonia-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E7%AC%AC%E4%BA%8C%E6%AC%A1-Composition-Animation-%E6%97%A0%E6%B3%95%E6%92%AD%E6%94%BE.html&quot;&gt;Avalonia 已知问题 第二次 Composition Animation 无法播放&lt;/a&gt;
&lt;!-- [Avalonia 已知问题 第二次 Composition Animation 无法播放 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18512022 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Avalonia-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E7%BB%A7%E6%89%BF%E6%BB%9A%E5%8A%A8%E6%9D%A1%E5%B0%86%E8%AE%A9%E9%87%8C%E5%B1%82%E6%8E%A7%E4%BB%B6%E6%97%A0%E6%B3%95%E8%8E%B7%E5%BE%97%E6%97%A0%E7%A9%B7%E5%A4%A7%E7%A9%BA%E9%97%B4.html&quot;&gt;Avalonia 已知问题 继承滚动条将让里层控件无法获得无穷大空间&lt;/a&gt;
&lt;!-- [Avalonia 已知问题 继承滚动条将让里层控件无法获得无穷大空间 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18766603 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Avalonia-%E5%8F%96%E6%B6%88%E7%AA%97%E5%8F%A3%E5%85%B3%E9%97%AD%E5%AF%BC%E8%87%B4-Linux-%E9%BA%92%E9%BA%9F%E7%B3%BB%E7%BB%9F%E6%97%A0%E6%B3%95%E5%85%B3%E6%9C%BA%E6%B3%A8%E9%94%80%E9%87%8D%E5%90%AF.html&quot;&gt;Avalonia 取消窗口关闭导致 Linux 麒麟系统无法关机注销重启&lt;/a&gt;
&lt;!-- [Avalonia 取消窗口关闭导致 Linux 麒麟系统无法关机注销重启 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18924837 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Avalonia-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E4%BD%BF%E7%94%A8-RenderTargetBitmap-%E6%88%AA%E5%9B%BE%E6%96%87%E6%9C%AC%E6%A8%A1%E7%B3%8A.html&quot;&gt;Avalonia 已知问题 使用 RenderTargetBitmap 截图文本模糊&lt;/a&gt;
&lt;!-- [Avalonia 已知问题 使用 RenderTargetBitmap 截图文本模糊 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18978159 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Avalonia-%E8%A7%A3%E5%86%B3%E6%B8%B2%E6%9F%93%E9%AB%98%E6%B8%85%E5%A4%A7%E5%9B%BE%E5%B8%A7%E7%8E%87%E4%B8%8B%E9%99%8D%E9%97%AE%E9%A2%98.html&quot;&gt;Avalonia 解决渲染高清大图帧率下降问题&lt;/a&gt;
&lt;!-- [Avalonia 解决渲染高清大图帧率下降问题 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19741763 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Avalonia-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E8%BF%87%E6%97%A9%E5%88%9B%E5%BB%BA-App-%E5%AF%B9%E8%B1%A1%E5%B0%86%E6%8A%9B%E5%87%BA-PlatformNotSupportedException-%E5%BC%82%E5%B8%B8.html&quot;&gt;Avalonia 已知问题 过早创建 App 对象将抛出 PlatformNotSupportedException 异常&lt;/a&gt;
&lt;!-- [Avalonia 已知问题 过早创建 App 对象将抛出 PlatformNotSupportedException 异常 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19819931 ) --&gt;&lt;/p&gt;

&lt;h4 id=&quot;cpf&quot;&gt;CPF&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%AD%A6%E4%B9%A0-CPF-%E6%A1%86%E6%9E%B6%E7%AC%94%E8%AE%B0-%E4%BA%86%E8%A7%A3-X11-%E7%AA%97%E5%8F%A3%E5%92%8C%E6%B6%88%E6%81%AF%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86.html&quot;&gt;学习 CPF 框架笔记 了解 X11 窗口和消息基础知识&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%AD%A6%E4%B9%A0-CPF-%E6%A1%86%E6%9E%B6%E7%AC%94%E8%AE%B0-%E4%BA%86%E8%A7%A3-X11-%E7%BB%98%E5%88%B6%E5%9B%BE%E7%89%87%E6%96%B9%E6%B3%95.html&quot;&gt;学习 CPF 框架笔记 了解 X11 绘制图片方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%AD%A6%E4%B9%A0-CPF-%E6%A1%86%E6%9E%B6%E7%AC%94%E8%AE%B0-%E4%BA%86%E8%A7%A3-X11-%E9%87%8C%E5%A6%82%E4%BD%95%E8%8E%B7%E5%8F%96%E8%A7%A6%E6%91%B8%E4%BF%A1%E6%81%AF.html&quot;&gt;dotnet 学习 CPF 框架笔记 了解 X11 里如何获取触摸信息&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;x11&quot;&gt;X11&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%AD%A6%E4%B9%A0-CPF-%E6%A1%86%E6%9E%B6%E7%AC%94%E8%AE%B0-%E4%BA%86%E8%A7%A3-X11-%E7%AA%97%E5%8F%A3%E5%92%8C%E6%B6%88%E6%81%AF%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86.html&quot;&gt;学习 CPF 框架笔记 了解 X11 窗口和消息基础知识&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E8%AE%BE%E7%BD%AE-X11-%E5%BA%94%E7%94%A8%E7%AA%97%E5%8F%A3%E8%83%8C%E6%99%AF%E9%80%8F%E6%98%8E.html&quot;&gt;dotnet C# 设置 X11 应用窗口背景透明&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%88%9B%E5%BB%BA-X11-%E5%BA%94%E7%94%A8%E6%97%B6%E8%AE%BE%E7%BD%AE%E7%AA%97%E5%8F%A3%E8%83%8C%E6%99%AF%E9%A2%9C%E8%89%B2.html&quot;&gt;dotnet C# 创建 X11 应用时设置窗口背景颜色&lt;/a&gt;
&lt;!-- [dotnet C# 创建 X11 应用时设置窗口背景颜色 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18225520 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%AD%A6%E4%B9%A0-CPF-%E6%A1%86%E6%9E%B6%E7%AC%94%E8%AE%B0-%E4%BA%86%E8%A7%A3-X11-%E7%BB%98%E5%88%B6%E5%9B%BE%E7%89%87%E6%96%B9%E6%B3%95.html&quot;&gt;学习 CPF 框架笔记 了解 X11 绘制图片方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-X11-%E5%A4%9A%E6%AC%A1%E8%B0%83%E7%94%A8-XPutImage-%E6%98%AF%E5%90%A6%E8%83%BD%E5%81%9A%E5%88%B0%E6%B8%B2%E6%9F%93%E5%90%8C%E6%AD%A5.html&quot;&gt;dotnet X11 多次调用 XPutImage 是否能做到渲染同步&lt;/a&gt;
&lt;!-- [dotnet X11 多次调用 XPutImage 是否能做到渲染同步 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18377330 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-X11-%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8-MIT-SHM-%E5%85%B1%E4%BA%AB%E5%86%85%E5%AD%98%E6%8E%A8%E9%80%81%E5%9B%BE%E7%89%87.html&quot;&gt;dotnet X11 简单使用 MIT-SHM 共享内存推送图片&lt;/a&gt;
&lt;!-- [dotnet X11 简单使用 MIT-SHM 共享内存推送图片 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18370811 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-X11-%E6%A0%88%E7%A9%BA%E9%97%B4%E8%A2%AB%E5%9B%9E%E6%94%B6%E5%AF%BC%E8%87%B4%E8%B0%83%E7%94%A8-XPutShmImage-%E9%97%AA%E9%80%80.html&quot;&gt;dotnet X11 栈空间被回收导致调用 XPutShmImage 闪退&lt;/a&gt;
&lt;!-- [dotnet X11 栈空间被回收导致调用 XPutShmImage 闪退 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18375092 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%AD%A6%E4%B9%A0-Avalonia-%E6%A1%86%E6%9E%B6%E7%AC%94%E8%AE%B0-%E5%A6%82%E4%BD%95%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA%E5%85%A8%E5%B1%8F%E7%BD%AE%E9%A1%B6%E7%9A%84-X11-%E5%BA%94%E7%94%A8%E7%AA%97%E5%8F%A3.html&quot;&gt;学习 Avalonia 框架笔记 如何创建一个全屏置顶的 X11 应用窗口&lt;/a&gt; &lt;a href=&quot;https://cloud.tencent.com/developer/article/2407503&quot;&gt;腾讯云&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E7%90%86%E8%A7%A3-X11-%E7%9A%84-24-%E4%BD%8D%E6%88%96-32-%E4%BD%8D%E8%89%B2%E6%B7%B1%E7%AA%97%E5%8F%A3.html&quot;&gt;dotnet 理解 X11 的 24 位或 32 位色深窗口&lt;/a&gt;
&lt;!-- [dotnet 理解 X11 的 24 位或 32 位色深窗口 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18299633 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-X11-%E5%BC%80%E5%8F%91%E7%AC%94%E8%AE%B0.html&quot;&gt;dotnet C# X11 开发笔记&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%A6%82%E4%BD%95%E4%BB%8E-Gtk-3-%E7%9A%84%E7%AA%97%E5%8F%A3%E5%88%B0%E5%AF%B9%E5%BA%94%E7%9A%84-X11-%E7%AA%97%E5%8F%A3.html&quot;&gt;dotnet 如何从 Gtk 3 的窗口到对应的 X11 窗口&lt;/a&gt;
&lt;!-- [dotnet 如何从 Gtk 3 的窗口到对应的 X11 窗口 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18192609 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-X11-%E7%AA%97%E5%8F%A3%E4%B9%8B%E9%97%B4%E5%8F%91%E9%80%81%E9%BC%A0%E6%A0%87%E6%B6%88%E6%81%AF-%E6%A8%A1%E6%8B%9F%E9%BC%A0%E6%A0%87%E8%BE%93%E5%85%A5.html&quot;&gt;dotnet X11 窗口之间发送鼠标消息 模拟鼠标输入&lt;/a&gt;
&lt;!-- [dotnet X11 窗口之间发送鼠标消息 模拟鼠标输入 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18192607 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%90%8E%E5%8F%B0%E7%BA%BF%E7%A8%8B%E5%8F%91%E9%80%81-X11-%E7%AA%97%E5%8F%A3%E6%B6%88%E6%81%AF.html&quot;&gt;dotnet 后台线程发送 X11 窗口消息&lt;/a&gt;
&lt;!-- [dotnet 后台线程发送 X11 窗口消息 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18192608 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%90%8E%E5%8F%B0%E7%BA%BF%E7%A8%8B%E8%AE%BE%E7%BD%AE-X11-%E7%AA%97%E5%8F%A3%E6%9C%80%E5%B0%8F%E5%8C%96.html&quot;&gt;dotnet 后台线程设置 X11 窗口最小化&lt;/a&gt;
&lt;!-- [dotnet 后台线程设置 X11 窗口最小化 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18192610 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%AD%A6%E4%B9%A0-Avalonia-%E6%A1%86%E6%9E%B6%E7%AC%94%E8%AE%B0-%E8%AE%BE%E7%BD%AE-X11-%E7%AA%97%E5%8F%A3%E4%BB%8E%E6%9C%80%E5%B0%8F%E5%8C%96%E7%8A%B6%E6%80%81%E8%BF%98%E5%8E%9F%E4%B8%BA%E6%AD%A3%E5%B8%B8%E7%8A%B6%E6%80%81.html&quot;&gt;学习 Avalonia 框架笔记 设置 X11 窗口从最小化状态还原为正常状态&lt;/a&gt;
&lt;!-- [学习 Avalonia 框架笔记 设置 X11 窗口从最小化状态还原为正常状态 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18197087 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AE%BE%E7%BD%AE-X11-%E5%BB%BA%E7%AB%8B%E7%AA%97%E5%8F%A3%E4%B9%8B%E9%97%B4%E7%9A%84%E7%88%B6%E5%AD%90%E5%85%B3%E7%B3%BB.html&quot;&gt;dotnet 设置 X11 建立窗口之间的父子关系&lt;/a&gt;
&lt;!-- [dotnet 设置 X11 建立窗口之间的父子关系 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18197088 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-X11-%E8%AE%BE%E7%BD%AE%E7%AA%97%E5%8F%A3%E9%BC%A0%E6%A0%87%E8%A7%A6%E6%91%B8%E5%91%BD%E4%B8%AD%E7%A9%BF%E9%80%8F.html&quot;&gt;dotnet X11 设置窗口鼠标触摸命中穿透&lt;/a&gt;
&lt;!-- [dotnet X11 设置窗口鼠标触摸命中穿透 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18204514 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%9C%A8-UNO-%E9%87%8C%E8%8E%B7%E5%8F%96-X11-%E7%AA%97%E5%8F%A3%E6%8C%87%E9%92%88%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet 在 UNO 里获取 X11 窗口指针的方法&lt;/a&gt;
&lt;!-- [dotnet 在 UNO 里获取 X11 窗口指针的方法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18207530 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-X11-%E7%AA%97%E5%8F%A3-Destroy-%E4%B9%8B%E5%90%8E%E6%98%AF%E5%90%A6%E5%8F%AF%E4%BB%A5%E5%86%8D%E6%AC%A1-Map-%E6%98%BE%E7%A4%BA.html&quot;&gt;dotnet X11 窗口 Destroy 之后是否可以再次 Map 显示&lt;/a&gt;
&lt;!-- [dotnet X11 窗口 Destroy 之后是否可以再次 Map 显示 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18358071 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-X11-%E8%B0%83%E7%94%A8-XRootWindow-%E6%98%AF%E5%90%A6%E8%80%97%E6%97%B6.html&quot;&gt;dotnet X11 调用 XRootWindow 是否耗时&lt;/a&gt;
&lt;!-- [dotnet X11 调用 XRootWindow 是否耗时 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18274612 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Avalonia-%E5%9C%A8-X11-%E4%B8%8B%E4%BD%BF%E7%94%A8%E8%BD%AF%E6%B8%B2%E6%9F%93%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;Avalonia 在 X11 下使用软渲染的方法&lt;/a&gt;
&lt;!-- [Avalonia 在 X11 下使用软渲染的方法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18317439 ) --&gt;&lt;/p&gt;

&lt;h5 id=&quot;x11-触摸相关&quot;&gt;X11 触摸相关&lt;/h5&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%AD%A6%E4%B9%A0-CPF-%E6%A1%86%E6%9E%B6%E7%AC%94%E8%AE%B0-%E4%BA%86%E8%A7%A3-X11-%E9%87%8C%E5%A6%82%E4%BD%95%E8%8E%B7%E5%8F%96%E8%A7%A6%E6%91%B8%E4%BF%A1%E6%81%AF.html&quot;&gt;dotnet 学习 CPF 框架笔记 了解 X11 里如何获取触摸信息&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AE%B0-X11-%E9%87%8C%E9%9D%A2%E8%A7%A6%E6%91%B8%E7%9A%84%E4%B8%80%E4%BA%9B%E8%A1%8C%E4%B8%BA.html&quot;&gt;记 X11 里面触摸的一些行为&lt;/a&gt;
&lt;!-- [记 X11 里面触摸的一些行为 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18468854 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-X11-%E7%9A%84%E5%A4%9A%E5%B1%8F%E8%A7%A6%E6%91%B8%E8%A1%8C%E4%B8%BA%E6%B5%8B%E8%AF%95.html&quot;&gt;dotnet X11 的多屏触摸行为测试&lt;/a&gt;
&lt;!-- [dotnet X11 的多屏触摸行为测试 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18333725 ) --&gt;&lt;/p&gt;

&lt;h5 id=&quot;x11-多屏&quot;&gt;X11 多屏&lt;/h5&gt;

&lt;p&gt;&lt;a href=&quot;/post/X11-%E4%BD%BF%E7%94%A8-XSetWMNormalHints-%E5%9B%BA%E5%AE%9A%E7%AA%97%E5%8F%A3%E6%89%80%E5%9C%A8%E7%9A%84%E5%B1%8F%E5%B9%95.html&quot;&gt;X11 使用 XSetWMNormalHints 固定窗口所在的屏幕&lt;/a&gt;
&lt;!-- [X11 使用 XSetWMNormalHints 固定窗口所在的屏幕 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19024401 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/X11-%E8%AE%BE%E7%BD%AE%E5%A4%9A%E5%B1%8F%E4%B8%8B%E7%AA%97%E5%8F%A3%E5%9C%A8%E5%93%AA%E4%B8%AA%E5%B1%8F%E5%B9%95%E4%B8%8A%E5%85%A8%E5%B1%8F.html&quot;&gt;X11 设置多屏下窗口在哪个屏幕上全屏&lt;/a&gt;
&lt;!-- [X11 设置多屏下窗口在哪个屏幕上全屏 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19027800 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-X11-%E8%8E%B7%E5%8F%96%E5%A4%9A%E5%B1%8F-edid-%E4%BF%A1%E6%81%AF.html&quot;&gt;dotnet X11 获取多屏 edid 信息&lt;/a&gt;
&lt;!-- [dotnet X11 获取多屏 edid 信息 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19033056 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-X11-%E7%9A%84%E5%A4%9A%E5%B1%8F%E8%A7%A6%E6%91%B8%E8%A1%8C%E4%B8%BA%E6%B5%8B%E8%AF%95.html&quot;&gt;dotnet X11 的多屏触摸行为测试&lt;/a&gt;
&lt;!-- [dotnet X11 的多屏触摸行为测试 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18333725 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;数据库&quot;&gt;数据库&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-core-%E4%BD%BF%E7%94%A8-ef-%E8%BF%81%E7%A7%BB%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98.html&quot;&gt;dotnet core 使用 ef 迁移常见问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%A6%82%E4%BD%95%E8%B0%83%E8%AF%95-SmartSql-%E7%9A%84%E5%AE%9E%E9%99%85%E6%89%A7%E8%A1%8C-SQL-%E8%AF%AD%E5%8F%A5.html&quot;&gt;dotnet 如何调试 SmartSql 的实际执行 SQL 语句&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%85%B3%E4%BA%8E-SmartSql-%E7%9A%84-SQL-%E8%AF%AD%E5%8F%A5%E7%9A%84%E5%B1%9E%E6%80%A7%E6%9B%BF%E6%8D%A2%E5%89%8D%E7%BC%80%E8%AF%B4%E6%98%8E.html&quot;&gt;dotnet 关于 SmartSql 的 SQL 语句的属性替换前缀说明&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-core-%E4%BD%BF%E7%94%A8-sqlite-%E9%83%A8%E7%BD%B2%E5%88%B0-Centos-%E6%9C%8D%E5%8A%A1%E5%99%A8.html&quot;&gt;dotnet core 使用 sqlite 部署到 Centos 服务器&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E7%A6%81%E7%94%A8-SQLite-%E7%9A%84-SQLiteFunction-%E6%89%AB%E6%8F%8F%E7%A8%8B%E5%BA%8F%E9%9B%86%E6%8F%90%E5%8D%87%E5%90%AF%E5%8A%A8%E6%80%A7%E8%83%BD.html&quot;&gt;dotnet 禁用 SQLite 的 SQLiteFunction 扫描程序集提升启动性能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/SQLite-%E7%94%B1%E4%BA%8E-mscoree.dll-%E6%8D%9F%E5%9D%8F%E5%AF%BC%E8%87%B4-BadImageFormatException-%E8%AF%95%E5%9B%BE%E5%8A%A0%E8%BD%BD%E6%A0%BC%E5%BC%8F%E4%B8%8D%E6%AD%A3%E7%A1%AE%E7%9A%84%E7%A8%8B%E5%BA%8F.html&quot;&gt;SQLite 由于 mscoree.dll 损坏导致 BadImageFormatException 试图加载格式不正确的程序&lt;/a&gt;
&lt;!-- [SQLite 由于 mscoree.dll 损坏导致 BadImageFormatException 试图加载格式不正确的程序 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18397604 ) --&gt;&lt;/p&gt;

&lt;h2 id=&quot;wpf&quot;&gt;WPF&lt;/h2&gt;

&lt;p&gt;记录 WPF 开发的杂烩： &lt;a href=&quot;/post/WPF-%E5%BC%80%E5%8F%91.html&quot;&gt;WPF 开发&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;入门的教程： &lt;a href=&quot;https://wpf-tutorial.com/zh/1/%E5%85%B3%E4%BA%8Ewpf/%E4%BB%80%E4%B9%88%E6%98%AFwpf/?tdsourcetag=s_pctim_aiomsg&quot;&gt;什么是WPF - The complete WPF tutorial&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-Application-Compatibility-switches-list.html&quot;&gt;WPF Application Compatibility switches list&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E9%80%9A%E8%BF%87-Switch.MS.Internal.EnableWeakEventMemoryImprovements-%E5%BC%80%E5%85%B3%E5%BC%80%E5%90%AF%E5%BC%B1%E4%BA%8B%E4%BB%B6%E5%86%85%E5%AD%98%E4%BC%98%E5%8C%96.html&quot;&gt;WPF 通过 Switch.MS.Internal.EnableWeakEventMemoryImprovements 开关开启弱事件内存优化&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-MAUI-%E7%9A%84%E8%87%AA%E7%BB%98%E5%88%B6%E9%80%BB%E8%BE%91.html&quot;&gt;WPF 使用 MAUI 的自绘制逻辑&lt;/a&gt; &lt;a href=&quot;https://www.cnblogs.com/lindexi/p/16395472.html&quot;&gt;博客园&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%88%87%E6%8D%A2%E4%B8%BB%E9%A2%98%E4%BD%BF%E7%94%A8-luna-%E5%A4%8D%E5%8F%A4%E7%89%88%E6%9C%AC.html&quot;&gt;WPF 切换主题使用 luna 复古版本&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%85%B3%E4%BA%8E%E5%B0%86-ManipulationDeltaEventArgs-%E7%9A%84-Manipulators-%E5%B1%9E%E6%80%A7%E8%BF%94%E5%9B%9E%E5%80%BC%E4%BF%AE%E6%94%B9%E4%B8%BA-ReadOnlyCollection-%E7%B1%BB%E5%9E%8B%E7%9A%84%E6%8F%90%E8%AE%AE.html&quot;&gt;WPF 关于将 ManipulationDeltaEventArgs 的 Manipulators 属性返回值修改为 ReadOnlyCollection 类型的提议&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnetCampus.UITest.WPF-%E4%B8%80%E4%B8%AA%E6%94%AF%E6%8C%81%E4%B8%AD%E6%96%87%E7%94%A8%E4%BE%8B%E7%9A%84%E7%95%8C%E9%9D%A2%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95%E6%A1%86%E6%9E%B6.html&quot;&gt;dotnetCampus.UITest.WPF 一个支持中文用例的界面单元测试框架&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/wpf-%E5%8D%95%E4%BE%8B.html&quot;&gt;wpf 单例&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%97%8B%E8%BD%AC%E5%A4%AA%E6%9E%81.html&quot;&gt;WPF 旋转太极&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%BC%95%E7%94%A8%E7%AC%AC%E4%B8%89%E6%96%B9%E5%BA%93%E7%9A%84%E6%8E%A7%E4%BB%B6%E5%9C%A8%E8%AE%BE%E8%AE%A1%E5%99%A8%E5%8A%A0%E4%B8%8A%E8%AE%BE%E8%AE%A1%E6%97%B6%E6%95%B0%E6%8D%AE%E5%92%8C%E5%B1%9E%E6%80%A7.html&quot;&gt;WPF 引用第三方库的控件在设计器加上设计时数据和属性&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%88%B6%E4%BD%9C%E4%B8%80%E4%B8%AA%E5%8D%A0%E7%94%A8%E6%96%87%E4%BB%B6%E7%9A%84%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7.html&quot;&gt;WPF 制作一个占用文件的测试工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-Microsoft.Toolkit.Wpf.UI.Controls-%E7%9A%84-InkCanvas-%E5%81%9A%E9%AB%98%E6%80%A7%E8%83%BD%E7%AC%94%E8%BF%B9%E5%BA%94%E7%94%A8.html&quot;&gt;WPF 使用 Microsoft.Toolkit.Wpf.UI.Controls 的 InkCanvas 做高性能笔迹应用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%9C%A8%E5%90%8E%E5%8F%B0%E4%BB%A3%E7%A0%81%E5%AE%9A%E4%B9%89-ResourceDictionary-%E8%B5%84%E6%BA%90%E5%AD%97%E5%85%B8.html&quot;&gt;WPF 在后台代码定义 ResourceDictionary 资源字典&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%90%AF%E5%8A%A8%E5%B1%8F%E5%B9%95%E9%94%AE%E7%9B%98.html&quot;&gt;WPF 启动屏幕键盘&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E9%99%84%E5%8A%A0%E5%B1%9E%E6%80%A7%E6%8F%90%E4%BE%9B%E6%9F%90%E4%B8%AA%E5%85%83%E7%B4%A0%E6%8B%A5%E6%9C%89%E6%8B%96%E6%8B%BD%E7%AA%97%E5%8F%A3%E7%9A%84%E5%8A%9F%E8%83%BD.html&quot;&gt;WPF 附加属性提供某个元素拥有拖拽窗口的功能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%BB%91%E5%AE%9A%E5%AF%86%E7%A0%81.html&quot;&gt;WPF 绑定密码&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/wpf-%E7%BB%91%E5%AE%9A-TextLength.html&quot;&gt;wpf 绑定 TextLength &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BF%AE%E5%A4%8D-ContextMenu-%E5%9C%A8%E5%BC%80%E5%90%AF-PerMonitorV2-%E5%90%8E%E6%89%80%E7%94%A8-DPI-%E9%94%99%E8%AF%AF.html&quot;&gt;WPF 修复 ContextMenu 在开启 PerMonitorV2 后所用 DPI 错误&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8RPC%E8%B0%83%E7%94%A8%E5%85%B6%E4%BB%96%E8%BF%9B%E7%A8%8B.html&quot;&gt;WPF 使用RPC调用其他进程&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E5%BC%80%E5%8F%91-dotnet-Remoting-%E7%A8%8B%E5%BA%8F.html&quot;&gt;WPF 从零开始开发 dotnet Remoting 程序&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AE%B0%E4%BB%8E-dotnet-framework-4.8-%E5%8D%87%E7%BA%A7%E5%88%B0-4.8.1-%E6%97%B6%E8%BF%90%E8%A1%8C%E7%9A%84-dotnet-remoting-%E7%A8%8B%E5%BA%8F%E5%87%BA%E7%8E%B0%E7%A9%BA%E5%BC%82%E5%B8%B8.html&quot;&gt;记从 dotnet framework 4.8 升级到 4.8.1 时运行的 dotnet remoting 程序出现空异常&lt;/a&gt;
&lt;!-- [记从 dotnet framework 4.8 升级到 4.8.1 时运行的 dotnet remoting 程序出现空异常 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18527092 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%BB%91%E5%AE%9A%E7%BB%A7%E6%89%BF%E7%9A%84%E6%A0%B7%E5%BC%8F%E6%8F%90%E7%A4%BA-%E5%8F%AA%E8%83%BD%E6%A0%B9%E6%8D%AE%E5%B8%A6%E6%9C%89%E5%9F%BA%E7%B1%BB%E5%9E%8B-IFrameworkInputElement-%E7%9A%84%E7%9B%AE%E6%A0%87%E7%B1%BB%E5%9E%8B%E7%9A%84-Style-%E6%A0%B7%E5%BC%8F.html&quot;&gt;WPF 绑定继承的样式提示 只能根据带有基类型 IFrameworkInputElement 的目标类型的 Style 样式&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%B8%BA%E4%BD%95-WPF-%E5%AF%B9-vcruntime140-%E6%9C%89%E5%BC%95%E7%94%A8.html&quot;&gt;为何 WPF 对 vcruntime140 有引用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%94%BB%E5%B8%83%E5%B7%A5%E5%85%B7%E6%A0%8F%E7%9A%84%E5%8F%AF%E6%89%A9%E5%B1%95%E8%AE%BE%E8%AE%A1.html&quot;&gt;WPF 画布工具栏的可扩展设计&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%BB%99%E4%BB%BB%E6%84%8F%E6%8E%A7%E4%BB%B6%E9%80%9A%E8%BF%87%E6%8C%89%E4%B8%8B%E7%A7%BB%E5%8A%A8%E6%8A%AC%E8%B5%B7%E5%B0%81%E8%A3%85%E7%82%B9%E5%87%BB%E4%BA%8B%E4%BB%B6.html&quot;&gt;WPF 给任意控件通过按下移动抬起封装点击事件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-Microsoft.Toolkit.Wpf.UI.Controls-%E7%9A%84-InkCanvas-%E6%97%B6%E5%8A%A0%E4%B8%8A%E8%83%8C%E6%99%AF%E8%89%B2%E5%92%8C%E6%8C%89%E9%92%AE%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 使用 Microsoft.Toolkit.Wpf.UI.Controls 的 InkCanvas 时加上背景色和按钮方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E9%BC%A0%E6%A0%87%E5%85%89%E6%A0%87%E5%A4%A7%E5%85%A8.html&quot;&gt;WPF 鼠标光标大全&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%A6%82%E4%BD%95%E6%89%BE%E5%88%B0%E8%B5%84%E6%BA%90%E6%96%87%E4%BB%B6%E8%B7%AF%E5%BE%84%E5%8C%85%E5%90%AB-%E5%8F%B7%E7%9A%84%E6%96%87%E4%BB%B6.html&quot;&gt;WPF 如何找到资源文件路径包含 # 号的文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E8%8E%B7%E5%8F%96%E4%B8%80%E4%B8%AA%E5%8F%AF%E7%94%A8%E7%9A%84%E7%AB%AF%E5%8F%A3%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet C# 获取一个可用的端口的方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%9A%84-DefaultEventAttribute-%E6%9C%89%E4%BB%80%E4%B9%88%E4%BD%9C%E7%94%A8.html&quot;&gt;WPF 的 DefaultEventAttribute 有什么作用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%80%9A%E8%BF%87-Elmish.WPF-%E4%BD%BF%E7%94%A8-F-%E7%BC%96%E5%86%99-WPF-%E5%BA%94%E7%94%A8.html&quot;&gt;dotnet 通过 Elmish.WPF 使用 F# 编写 WPF 应用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%88%97%E8%A1%A8%E6%8E%A7%E4%BB%B6%E6%95%B0%E6%8D%AE%E6%BA%90%E7%BB%91%E5%AE%9A%E5%A4%9A%E4%B8%AA%E6%95%B0%E6%8D%AE%E9%9B%86%E5%90%88%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 列表控件数据源绑定多个数据集合方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-ManipulationProcessor2D-%E7%BA%AF%E6%95%B0%E5%AD%A6%E8%AE%A1%E7%AE%97%E6%96%B9%E5%BC%8F%E6%8F%90%E4%BE%9B%E5%A4%9A%E7%82%B9%E6%BC%AB%E6%B8%B8%E5%85%83%E7%B4%A0%E5%8A%9F%E8%83%BD.html&quot;&gt;WPF 使用 ManipulationProcessor2D 纯数学计算方式提供多点漫游元素功能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-XmlDataProvider-%E6%8F%90%E4%BE%9B%E6%95%B0%E6%8D%AE.html&quot;&gt;WPF 使用 XmlDataProvider 提供数据&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%8C%89%E9%92%AE-Button-%E7%9A%84-IsEnabled-%E5%B1%9E%E6%80%A7%E5%AF%B9-WindowChrome-%E7%9A%84-IsHitTestVisibleInChrome-%E7%9A%84%E5%BD%B1%E5%93%8D.html&quot;&gt;WPF 按钮 Button 的 IsEnabled 属性对 WindowChrome 的 IsHitTestVisibleInChrome 的影响&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%A6%82%E4%BD%95%E5%9C%A8%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90%E5%AE%9A%E4%B9%89%E5%AD%97%E4%BD%93%E5%A4%A7%E5%B0%8F.html&quot;&gt;WPF 如何在静态资源定义字体大小&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%BB%99%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E6%B7%BB%E5%8A%A0%E6%B0%B4%E5%8D%B0.html&quot;&gt;WPF 给应用程序添加水印&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-WpfAnalyzers-%E8%BE%85%E5%8A%A9%E5%88%86%E6%9E%90-WPF-%E5%BA%94%E7%94%A8%E4%BB%A3%E7%A0%81%E7%BC%BA%E9%99%B7.html&quot;&gt;dotnet 使用 WpfAnalyzers 辅助分析 WPF 应用代码缺陷&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%88%97%E8%A1%A8%E5%8F%B3%E9%94%AE%E8%8F%9C%E5%8D%95%E6%AF%94%E8%BE%83%E7%AC%A6%E5%90%88-MVVM-%E7%9A%84%E5%91%BD%E4%BB%A4%E7%BB%91%E5%AE%9A%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 列表右键菜单比较符合 MVVM 的命令绑定方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%B8%8B%E6%8B%89%E6%A1%86%E9%80%89%E9%A1%B9%E5%81%9A%E9%BC%A0%E6%A0%87-Hover-%E9%A2%84%E8%A7%88%E6%95%88%E6%9E%9C.html&quot;&gt;WPF 下拉框选项做鼠标 Hover 预览效果&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%BB%99-Grid-%E7%9A%84%E8%BE%85%E5%8A%A9%E6%96%B9%E6%B3%95-%E6%B7%BB%E5%8A%A0%E8%A1%8C%E5%88%97%E5%90%8D%E7%A7%B0%E7%BB%91%E5%AE%9A.html&quot;&gt;WPF 给 Grid 的辅助方法 添加行列名称绑定&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%BB%99%E7%B1%BB%E5%BA%93%E8%AE%BE%E7%BD%AE%E8%AE%BE%E8%AE%A1%E6%97%B6%E4%BD%BF%E7%94%A8%E7%9A%84%E8%B5%84%E6%BA%90%E5%AD%97%E5%85%B8.html&quot;&gt;WPF 给类库设置设计时使用的资源字典&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%BD%A2%E7%8A%B6%E7%9A%84-StrokeThickness-%E5%B1%9E%E6%80%A7%E5%AF%B9%E8%BE%B9%E6%A1%86%E7%9A%84%E5%BD%B1%E5%93%8D.html&quot;&gt;WPF 形状的 StrokeThickness 属性对边框的影响&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%8E%B7%E5%8F%96%E8%BF%9B%E7%A8%8B%E5%90%AF%E5%8A%A8%E5%88%B0%E5%BD%93%E5%89%8D%E7%8E%B0%E5%9C%A8%E7%9A%84%E6%97%B6%E9%97%B4.html&quot;&gt;WPF 获取进程启动到当前现在的时间&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B0%86-docx-%E7%9A%84-Word-%E6%96%87%E4%BB%B6%E8%BD%AC%E6%8D%A2%E4%B8%BA-FlowDocument-%E6%98%BE%E7%A4%BA.html&quot;&gt;WPF 将 docx 的 Word 文件转换为 FlowDocument 显示&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%B8%BA%E4%BD%95%E4%B8%8D%E8%A6%81%E9%87%8D%E5%86%99%E9%BB%98%E8%AE%A4-string-%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84-DataTemplate-%E6%95%B0%E6%8D%AE%E6%A8%A1%E7%89%88.html&quot;&gt;WPF 为何不要重写默认 string 字符串的 DataTemplate 数据模版&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%AE%BE%E7%BD%AE%E5%85%83%E7%B4%A0%E4%B8%BA-Collapsed-%E6%98%AF%E5%90%A6%E4%BC%9A%E5%88%9B%E5%BB%BA%E6%AD%A4%E5%85%83%E7%B4%A0.html&quot;&gt;WPF 设置元素为 Collapsed 是否会创建此元素&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%AE%BE%E7%BD%AE%E7%AA%97%E5%8F%A3%E4%B8%8D%E8%B7%9F%E9%9A%8F%E8%A7%A6%E6%91%B8%E6%83%AF%E6%80%A7%E6%8B%96%E5%8A%A8%E6%8A%96%E5%8A%A8.html&quot;&gt;WPF 设置窗口不跟随触摸惯性拖动抖动&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%AE%BE%E7%BD%AE%E7%AE%A1%E7%90%86%E5%91%98%E6%9D%83%E9%99%90%E5%90%AF%E5%8A%A8.html&quot;&gt;WPF 设置管理员权限启动&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%BB%B6%E8%BF%9F%E5%8A%A0%E8%BD%BD.html&quot;&gt;WPF 延迟加载&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%BC%80%E5%8F%91%E8%87%AA%E5%8A%A8%E5%BC%80%E6%9C%BA%E5%90%AF%E5%8A%A8%E7%A8%8B%E5%BA%8F.html&quot;&gt;WPF 开发自动开机启动程序&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E9%9A%90%E8%97%8F%E7%B3%BB%E7%BB%9F%E7%AA%97%E5%8F%A3%E8%8F%9C%E5%8D%95.html&quot;&gt;WPF 隐藏系统窗口菜单&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BB%8E%E9%94%AE%E7%9B%98%E4%BA%8B%E4%BB%B6-KeyEventArgs-%E9%87%8C%E8%8E%B7%E5%8F%96-Scan-Code-%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 从键盘事件 KeyEventArgs 里获取 Scan Code 的方法&lt;/a&gt;
&lt;!-- [WPF 从键盘事件 KeyEventArgs 里获取 Scan Code 的方法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18176388 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/wpf-%E7%BB%91%E5%AE%9A-DataGridTextColumn.html&quot;&gt;wpf 绑定 DataGridTextColumn &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/wpf-DoEvents.html&quot;&gt;wpf DoEvents &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%BF%90%E8%A1%8C%E6%97%B6%E8%BF%81%E7%A7%BB-EF-Core-%E6%95%B0%E6%8D%AE%E5%BA%93.html&quot;&gt;WPF 运行时迁移 EF Core 数据库&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%BD%BB%E9%87%8F%E7%BA%A7-MVVM-%E6%A1%86%E6%9E%B6%E5%85%A5%E9%97%A8-2.1.2.html&quot;&gt;WPF 轻量级 MVVM 框架入门 2.1.2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%BB%91%E5%AE%9A%E5%91%BD%E4%BB%A4%E5%9C%A8-MVVM-%E7%9A%84-CanExecute-%E5%92%8C-Execute-%E5%9C%A8%E6%8C%89%E9%92%AE%E7%82%B9%E5%87%BB%E9%83%BD%E6%B2%A1%E8%A7%A6%E5%8F%91%E5%8F%AF%E8%83%BD%E7%9A%84%E5%8E%9F%E5%9B%A0.html&quot;&gt;WPF 绑定命令在 MVVM 的 CanExecute 和 Execute 在按钮点击都没触发可能的原因&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%A6%82%E4%BD%95%E5%9C%A8%E7%BB%91%E5%AE%9A%E5%A4%B1%E8%B4%A5%E5%BC%82%E5%B8%B8.html&quot;&gt;WPF 如何在绑定失败异常&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%85%83%E7%B4%A0%E8%A3%81%E5%89%AA-Clip-%E5%B1%9E%E6%80%A7.html&quot;&gt;WPF 元素裁剪 Clip 属性&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%89%98%E7%9B%98%E6%98%BE%E7%A4%BA.html&quot;&gt;WPF 托盘显示&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%A6%82%E4%BD%95%E5%88%A4%E6%96%AD%E4%B8%80%E4%B8%AA%E6%8E%A7%E4%BB%B6%E5%9C%A8%E6%BB%9A%E5%8A%A8%E6%9D%A1%E7%9A%84%E9%87%8C%E9%9D%A2%E6%98%AF%E7%94%A8%E6%88%B7%E5%8F%AF%E8%A7%81.html&quot;&gt;WPF 如何判断一个控件在滚动条的里面是用户可见&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%BB%91%E5%AE%9A%E7%9A%84%E9%BB%98%E8%AE%A4%E6%A8%A1%E5%BC%8F.html&quot;&gt;WPF 绑定的默认模式&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B0%81%E8%A3%85-dotnet-remoting-%E8%B0%83%E7%94%A8%E5%85%B6%E4%BB%96%E8%BF%9B%E7%A8%8B.html&quot;&gt;WPF 封装 dotnet remoting 调用其他进程&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%88%97%E8%A1%A8%E8%87%AA%E5%8A%A8%E6%8D%A2%E8%A1%8C.html&quot;&gt;WPF 列表自动换行&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%A6%82%E4%BD%95%E5%BB%BA%E7%AB%8B%E8%87%AA%E5%B7%B1%E7%9A%84-3d-gis-%E7%A8%8B%E5%BA%8F.html&quot;&gt;WPF 如何建立自己的 3d gis 程序&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%9C%A8%E7%BB%91%E5%AE%9A%E8%A1%A8%E8%BE%BE%E5%BC%8F%E6%B7%BB%E5%8A%A0%E8%AE%A1%E7%AE%97.html&quot;&gt;WPF 在绑定表达式添加计算&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BF%AE%E6%94%B9-ItemContainerStyle-%E9%BC%A0%E6%A0%87%E7%A7%BB%E5%8A%A8%E5%88%B0%E6%9C%AA%E9%80%89%E4%B8%AD%E9%A1%B9%E6%95%88%E6%9E%9C%E5%92%8C%E9%80%89%E4%B8%AD%E9%A1%B9%E8%83%8C%E6%99%AF.html&quot;&gt;WPF 修改 ItemContainerStyle 鼠标移动到未选中项效果和选中项背景&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%8E%B7%E5%8F%96%E4%B8%8B%E8%BD%BD%E5%86%85%E5%AE%B9%E9%95%BF%E5%BA%A6.html&quot;&gt;WPF 获取下载内容长度&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%8B%96%E5%8A%A8%E6%BB%9A%E5%8A%A8.html&quot;&gt;WPF 拖动滚动&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%87%AA%E5%AE%9A%E4%B9%89-TextBoxView-%E7%9A%84-Margin-%E5%A4%A7%E5%B0%8F.html&quot;&gt;WPF 自定义 TextBoxView 的 Margin 大小&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%BC%80%E5%8F%91%E8%87%AA%E5%8A%A8%E5%88%A0%E9%99%A4%E8%BD%AF%E4%BB%B6.html&quot;&gt;WPF 开发自动删除软件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E9%BC%A0%E6%A0%87%E7%A7%BB%E5%8A%A8%E5%88%B0%E5%88%97%E8%A1%A8%E4%B8%8A-%E6%98%BE%E7%A4%BA%E5%88%97%E8%A1%A8%E5%9B%BE%E6%A0%87.html&quot;&gt;WPF 鼠标移动到列表上 显示列表图标&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%9C%81%E5%B8%82%E5%8E%BF3%E7%BA%A7%E8%81%94%E5%8A%A8.html&quot;&gt;WPF 省市县3级联动&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%A5%BD%E7%9C%8B%E7%9A%84%E7%9F%A2%E9%87%8F%E5%9B%BE%E6%A0%87.html&quot;&gt;WPF 好看的矢量图标&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%8F%AF%E8%8E%B7%E5%BE%97%E7%84%A6%E7%82%B9%E5%B1%9E%E6%80%A7.html&quot;&gt;WPF 可获得焦点属性&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%88%A4%E6%96%ADUSB%E6%8F%92%E6%8B%94.html&quot;&gt;WPF 判断USB插拔&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BF%AE%E6%94%B9%E6%8C%89%E9%92%AE%E6%8C%89%E4%B8%8B%E7%9A%84%E9%A2%9C%E8%89%B2.html&quot;&gt;WPF 修改按钮按下的颜色&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%AF%BB%E5%8F%96%E7%A1%AC%E4%BB%B6%E5%BA%8F%E5%88%97%E5%8F%B7.html&quot;&gt;WPF 读取硬件序列号&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-WNetUseConnection-%E8%BF%9E%E6%8E%A5-SMB-%E7%BD%91%E7%BB%9C%E8%B5%84%E6%BA%90.html&quot;&gt;WPF 使用 WNetUseConnection 连接 SMB 网络资源&lt;/a&gt;
&lt;!-- [WPF 使用 WNetUseConnection 连接 SMB 网络资源 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19030487 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%B5%84%E6%BA%90%E5%86%BB%E7%BB%93.html&quot;&gt;WPF 资源冻结&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%BC%82%E5%B8%B8-NativeWPFDLLLoader.LoadNativeWPFDLL.html&quot;&gt;WPF 异常 NativeWPFDLLLoader.LoadNativeWPFDLL&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%8F%AA%E5%85%81%E8%AE%B8%E6%89%93%E5%BC%80%E4%B8%80%E4%B8%AA%E5%AE%9E%E4%BE%8B.html&quot;&gt;WPF 只允许打开一个实例&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-Pandoc-%E6%8A%8A-Markdown-%E8%BD%AC-Docx.html&quot;&gt;WPF 使用 Pandoc 把 Markdown 转 Docx&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E7%AE%80%E5%8D%95%E6%96%B9%E6%B3%95%E5%9C%A8%E4%B8%80%E4%B8%AA%E8%BF%9B%E7%A8%8B%E5%86%85%E5%90%8C%E6%97%B6%E8%B7%91%E8%B5%B7-WPF-%E5%92%8C-ASP.NET-Core-%E6%A1%86%E6%9E%B6.html&quot;&gt;dotnet 简单方法在一个进程内同时跑起 WPF 和 ASP.NET Core 框架&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%BC%95%E7%94%A8-ASP.NET-Core-%E7%9A%84-AOT-%E7%89%88%E6%9C%AC.html&quot;&gt;WPF 引用 ASP.NET Core 的 AOT 版本&lt;/a&gt;
&lt;!-- [WPF 引用 ASP.NET Core 的 AOT 版本 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19049877 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BB%8E-RGB-%E5%AD%97%E7%AC%A6%E4%B8%B2%E8%BD%AC%E7%BA%AF%E8%89%B2%E9%A2%9C%E8%89%B2%E7%94%BB%E5%88%B7%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 从 RGB 字符串转纯色颜色画刷的方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95.html&quot;&gt;WPF 性能测试&lt;/a&gt;
&lt;!-- [WPF 性能测试 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/17678694.html ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%90%AF%E5%8A%A8%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96-%E5%9C%A8-EnsureHandle-%E4%B9%8B%E5%89%8D%E8%AE%BE%E7%BD%AE-WindowStyle-%E6%8F%90%E5%8D%87%E6%80%A7%E8%83%BD.html&quot;&gt;WPF 启动性能优化 在 EnsureHandle 之前设置 WindowStyle 提升性能&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;wpf-应用开发&quot;&gt;WPF 应用开发&lt;/h3&gt;

&lt;p&gt;这是记录我用 WPF 开发的一些应用&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%88%B6%E4%BD%9C%E4%B8%80%E4%B8%AA%E5%8A%A0%E5%AF%86%E6%96%87%E4%BB%B6%E5%A4%B9%E5%BA%94%E7%94%A8.html&quot;&gt;WPF 制作一个加密文件夹应用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%AE%80%E5%8D%95%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E6%94%AF%E6%8C%81%E5%88%A0%E9%99%A4%E8%87%AA%E8%BA%AB%E7%9A%84%E5%BA%94%E7%94%A8.html&quot;&gt;WPF 简单实现一个支持删除自身的应用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B0%9D%E8%AF%95%E4%BD%BF%E7%94%A8-WinML-%E5%81%9A%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%9A%84%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB%E5%BA%94%E7%94%A8.html&quot;&gt;WPF 尝试使用 WinML 做一个简单的手写数字识别应用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%81%9A%E4%B8%80%E4%B8%AA%E8%B6%85%E7%BA%A7%E7%AE%80%E5%8D%95%E7%9A%84-1024-%E6%95%B0%E5%AD%97%E6%8E%A5%E9%BE%99%E6%B8%B8%E6%88%8F.html&quot;&gt;WPF 做一个超级简单的 1024 数字接龙游戏&lt;/a&gt;
&lt;!-- [WPF 做一个超级简单的 1024 数字接龙游戏 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18264294 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8%E5%BF%AB%E6%8D%B7%E9%94%AE%E6%96%B9%E5%BC%8F%E5%88%B6%E4%BD%9C%E7%AE%80%E6%98%93%E7%9A%84-Word-%E4%B8%8A%E7%9A%84-Latex-%E8%BE%93%E5%85%A5%E6%B3%95.html&quot;&gt;WPF 使用快捷键方式制作简易的 Word 上的 Latex 输入法&lt;/a&gt;
&lt;!-- [WPF 使用快捷键方式制作简易的 Word 上的 Latex 输入法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18498720 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%81%9A%E4%B8%80%E4%B8%AA%E5%8E%8B%E7%BC%A9%E5%8C%85%E5%92%8C%E8%A7%A3%E5%8E%8B%E7%BC%A9%E5%87%BA%E6%9D%A5%E7%9A%84%E6%96%87%E4%BB%B6%E5%A4%B9%E5%86%85%E5%AE%B9%E5%AF%B9%E6%AF%94%E5%B7%A5%E5%85%B7.html&quot;&gt;dotnet C# 做一个压缩包和解压缩出来的文件夹内容对比工具&lt;/a&gt;
&lt;!-- [dotnet C# 做一个压缩包和解压缩出来的文件夹内容对比工具 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18817811 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;wpf-触摸相关&quot;&gt;WPF 触摸相关&lt;/h3&gt;

&lt;p&gt;我写了很多 WPF 触摸相关的博客，请参阅：&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%A7%A6%E6%91%B8%E7%9B%B8%E5%85%B3.html&quot;&gt;WPF 触摸相关&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-dotnet-core-%E5%A6%82%E4%BD%95%E5%BC%80%E5%90%AF-Pointer-%E6%B6%88%E6%81%AF%E7%9A%84%E6%94%AF%E6%8C%81.html&quot;&gt;WPF dotnet core 如何开启 Pointer 消息的支持&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%A6%82%E4%BD%95%E7%A1%AE%E5%AE%9A%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E5%BC%80%E5%90%AF%E4%BA%86-Pointer-%E8%A7%A6%E6%91%B8%E6%B6%88%E6%81%AF%E7%9A%84%E6%94%AF%E6%8C%81.html&quot;&gt;WPF 如何确定应用程序开启了 Pointer 触摸消息的支持&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-%E4%BB%8E-WM_POINTER-%E6%B6%88%E6%81%AF%E5%88%B0-Touch-%E4%BA%8B%E4%BB%B6.html&quot;&gt;dotnet 读 WPF 源代码笔记 从 WM_POINTER 消息到 Touch 事件&lt;/a&gt;
&lt;!-- [dotnet 读 WPF 源代码笔记 从 WM_POINTER 消息到 Touch 事件 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18403860 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%BC%80%E5%90%AF-ScrollViewer-%E7%9A%84%E8%A7%A6%E6%91%B8%E6%BB%9A%E5%8A%A8.html&quot;&gt;WPF 开启 ScrollViewer 的触摸滚动&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E9%80%9A%E8%BF%87-WindowsAppSDK-%E4%BD%BF%E7%94%A8-WinRT-%E7%9A%84%E6%89%8B%E5%86%99%E8%AF%86%E5%88%AB%E5%8A%9F%E8%83%BD.html&quot;&gt;WPF 通过 WindowsAppSDK 使用 WinRT 的手写识别功能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%A7%A6%E6%91%B8%E4%B8%8B%E5%A6%82%E4%BD%95%E7%BB%99-StylusPointCollection-%E6%B7%BB%E5%8A%A0%E7%82%B9.html&quot;&gt;WPF 触摸下如何给 StylusPointCollection 添加点&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E9%80%9A%E8%BF%87-GetMessageExtraInfo-%E6%96%B9%E6%B3%95%E8%8E%B7%E5%8F%96%E5%BD%93%E5%89%8D%E6%94%B6%E5%88%B0%E7%9A%84%E9%BC%A0%E6%A0%87%E6%B6%88%E6%81%AF%E6%98%AF%E5%90%A6%E7%94%B1%E8%A7%A6%E6%91%B8%E8%BD%AC%E6%8D%A2%E8%BF%87%E6%9D%A5.html&quot;&gt;WPF 通过 GetMessageExtraInfo 方法获取当前收到的鼠标消息是否由触摸转换过来&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%AE%B0%E4%B8%80%E4%B8%AA%E7%89%B9%E5%88%AB%E7%AE%80%E5%8D%95%E7%9A%84%E7%82%B9%E9%9B%86%E6%BB%A4%E6%B3%A2%E5%B9%B3%E6%BB%91%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 记一个特别简单的点集滤波平滑方法&lt;/a&gt;
&lt;!-- [WPF 记一个特别简单的点集滤波平滑方法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18387840 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BB%8E%E8%A3%B8-Win-32-%E7%9A%84-WM_Pointer-%E6%B6%88%E6%81%AF%E8%8E%B7%E5%8F%96%E8%A7%A6%E6%91%B8%E7%82%B9%E7%BB%98%E5%88%B6%E7%AC%94%E8%BF%B9.html&quot;&gt;WPF 从裸 Win 32 的 WM_Pointer 消息获取触摸点绘制笔迹&lt;/a&gt;
&lt;!-- [WPF 从裸 Win 32 的 WM_Pointer 消息获取触摸点绘制笔迹 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18390983 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E9%80%9A%E8%BF%87-GetRawPointerDeviceData-%E4%BB%8E-WM_POINTER-%E6%B6%88%E6%81%AF%E8%A7%A6%E6%91%B8%E8%A3%B8%E6%95%B0%E6%8D%AE.html&quot;&gt;WPF 通过 GetRawPointerDeviceData 从 WM_POINTER 消息触摸裸数据&lt;/a&gt;
&lt;!-- [WPF 通过 GetRawPointerDeviceData 从 WM_POINTER 消息触摸裸数据 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18890017 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E7%9B%91%E5%90%AC-WMI-%E4%BA%8B%E4%BB%B6%E5%AF%BC%E8%87%B4%E8%A7%A6%E6%91%B8%E5%A4%B1%E6%95%88.html&quot;&gt;WPF 已知问题 监听 WMI 事件导致触摸失效&lt;/a&gt;
&lt;!-- [WPF 已知问题 监听 WMI 事件导致触摸失效 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18407581 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-Main-thread-gets-a-deadlock-when-stylus-input-thread-is-waiting-for-the-window-to-close.html&quot;&gt;WPF Main thread gets a deadlock when stylus input thread is waiting for the window to close&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-shows-that-some-windows-in-multithreading-will-be-locked-in-the-PenThreadWorker-constructor-when-the-application-starts.html&quot;&gt;WPF shows that some windows in multithreading will be locked in the PenThreadWorker constructor when the application starts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-will-break-when-an-exception-be-throw-in-the-StylusPlugIn.html&quot;&gt;WPF will break when an exception be throw in the StylusPlugIn&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;控件&quot;&gt;控件&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-checkbox%E6%96%87%E5%AD%97%E4%B8%8B%E6%8E%89.html&quot;&gt;WPF checkbox文字下掉&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-popup%E7%BD%AE%E9%A1%B6.html&quot;&gt;WPF popup置顶&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%A6%82%E4%BD%95%E7%BB%99-Grid-%E7%9A%84%E6%9F%90%E4%B8%80%E8%A1%8C%E6%B7%BB%E5%8A%A0%E8%83%8C%E6%99%AF%E8%89%B2.html&quot;&gt;WPF 如何给 Grid 的某一行添加背景色&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%AE%A9-TextBox-%E6%94%AF%E6%8C%81%E6%B0%B4%E5%B9%B3%E6%BB%9A%E5%8A%A8.html&quot;&gt;WPF 让 TextBox 支持水平滚动&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%8E%A7%E4%BB%B6-Content-%E7%9A%84%E5%86%85%E5%AE%B9%E4%B8%8D%E6%98%BE%E7%A4%BA%E4%B8%8B%E5%88%92%E7%BA%BF%E5%AD%97%E7%AC%A6%E4%B8%B2.html&quot;&gt;WPF 控件 Content 的内容不显示下划线字符串&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%9C%A8image%E6%8E%A7%E4%BB%B6%E7%94%A8%E9%BC%A0%E6%A0%87%E6%8B%96%E6%8B%BD%E5%87%BA%E7%9F%A9%E5%BD%A2.html&quot;&gt;WPF 在image控件用鼠标拖拽出矩形&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-ItemsPanel-%E4%BF%AE%E6%94%B9%E6%96%B9%E5%90%91.html&quot;&gt;WPF 使用 ItemsPanel 修改方向&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-ListBox-%E7%9A%84%E9%80%89%E6%8B%A9.html&quot;&gt;WPF ListBox 的选择&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%A7%A3%E5%86%B3-ListView-%E7%9A%84%E6%BB%9A%E5%8A%A8%E6%9D%A1%E4%B8%8D%E6%98%BE%E7%A4%BA.html&quot;&gt;WPF 解决 ListView 的滚动条不显示&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;自定义控件&quot;&gt;自定义控件&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%8E%A7%E4%BB%B6%E7%BB%A7%E6%89%BF%E6%A0%91.html&quot;&gt;WPF 控件继承树&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%87%AA%E5%AE%9A%E4%B9%89%E6%8E%A7%E4%BB%B6%E5%85%A5%E9%97%A8-%E5%8F%AF%E9%87%8D%E5%86%99%E7%9A%84%E5%90%84%E4%B8%AA%E6%96%B9%E6%B3%95%E6%88%96%E5%B1%9E%E6%80%A7%E7%9A%84%E6%84%8F%E4%B9%89.html&quot;&gt;WPF 自定义控件入门 可重写的各个方法或属性的意义&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%87%AA%E5%AE%9A%E4%B9%89%E6%8E%A7%E4%BB%B6%E5%85%A5%E9%97%A8-Focusable-%E4%B8%8E%E7%84%A6%E7%82%B9.html&quot;&gt;WPF 自定义控件入门 Focusable 与焦点&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%89%8B%E7%BB%98%E5%AF%B9%E7%A7%B0%E5%9B%BE%E5%BD%A2%E6%8E%A7%E4%BB%B6.html&quot;&gt;WPF 手绘对称图形控件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%BB%91%E5%8A%A8%E4%BF%AE%E6%94%B9%E9%9F%B3%E9%87%8F%E6%8E%A7%E4%BB%B6.html&quot;&gt;WPF 滑动修改音量控件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1Ui4y1a717/?vd_source=822993b9bf3248de16ece4f9f39499ea&quot;&gt;【收藏】 WPF教学 WPF做出这样丝滑的动画尽然如此简单？ Magic Navigation Bar 导航栏 哔哩哔哩_bilibili&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;控件库&quot;&gt;控件库&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-HandyControl-%E7%BB%99-ListView-%E6%B7%BB%E5%8A%A0%E6%BC%82%E4%BA%AE%E7%9A%84%E8%A1%A8%E5%A4%B4%E6%95%88%E6%9E%9C.html&quot;&gt;WPF 使用 HandyControl 给 ListView 添加漂亮的表头效果&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-HandyControl-%E5%B7%B2%E6%94%AF%E6%8C%81%E7%BB%99%E4%BB%BB%E6%84%8F%E6%8E%A7%E4%BB%B6%E9%80%9A%E8%BF%87%E6%8C%89%E4%B8%8B%E7%A7%BB%E5%8A%A8%E6%8A%AC%E8%B5%B7%E5%B0%81%E8%A3%85%E7%82%B9%E5%87%BB%E4%BA%8B%E4%BB%B6.html&quot;&gt;WPF HandyControl 已支持给任意控件通过按下移动抬起封装点击事件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%94%A8-AvalonEdit-%E5%BC%80%E5%8F%91%E7%AE%80%E5%8D%95%E7%9A%84%E4%BB%A3%E7%A0%81%E7%BC%96%E8%BE%91%E5%99%A8-%E6%94%AF%E6%8C%81%E9%AB%98%E4%BA%AE%E8%87%AA%E5%8A%A8%E6%8F%90%E7%A4%BA.html&quot;&gt;WPF 用 AvalonEdit 开发简单的代码编辑器 支持高亮自动提示&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%BC%82%E4%BA%AE%E7%9A%84%E7%8E%B0%E4%BB%A3%E5%8C%96%E6%8E%A7%E4%BB%B6-%E6%96%B0-ModernWPF-%E7%95%8C%E9%9D%A2%E5%BA%93.html&quot;&gt;WPF 漂亮的现代化控件 新 ModernWPF 界面库&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8%E5%9F%BA%E4%BA%8E-Chromium-%E5%86%85%E6%A0%B8%E7%9A%84-Microsoft-Edge-%E5%B0%81%E8%A3%85%E7%9A%84-WebView2-%E6%8E%A7%E4%BB%B6.html&quot;&gt;WPF 使用基于 Chromium 内核的 Microsoft Edge 封装的 WebView2 控件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-MyScript-%E7%9A%84-IInk-%E5%81%9A%E6%89%8B%E5%86%99%E8%AF%86%E5%88%AB.html&quot;&gt;WPF 使用 MyScript 的 IInk 做手写识别&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;布局&quot;&gt;布局&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B8%83%E5%B1%80-%E5%9C%A8%E6%9C%89%E9%99%90%E7%A9%BA%E9%97%B4%E5%86%85%E8%AE%A9%E4%B8%A4%E4%B8%AA%E5%85%83%E7%B4%A0%E5%B0%BD%E5%8F%AF%E8%83%BD%E6%92%91%E5%BC%80%E7%9A%84%E4%BE%8B%E5%AD%90.html&quot;&gt;WPF 布局 在有限空间内让两个元素尽可能撑开的例子&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-UNO-%E6%B5%8B%E8%AF%95%E5%9B%BA%E5%AE%9A%E5%B0%BA%E5%AF%B8%E4%B8%94%E6%B0%B4%E5%B9%B3%E5%92%8C%E5%9E%82%E7%9B%B4%E5%AF%B9%E9%BD%90%E8%AE%BE%E7%BD%AE-Stretch-%E7%9A%84%E5%85%83%E7%B4%A0%E5%9C%A8%E5%AE%B9%E5%99%A8%E5%86%85%E7%9A%84%E5%B8%83%E5%B1%80%E8%A1%8C%E4%B8%BA.html&quot;&gt;WPF 测试固定尺寸且水平和垂直对齐设置 Stretch 的元素在容器内的布局行为&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%AD%89%E8%B7%9D%E5%B8%83%E5%B1%80.html&quot;&gt;WPF 等距布局&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BB%80%E4%B9%88%E6%97%B6%E5%80%99-VisualTreeHelper.GetDescendantBounds-%E5%B0%86%E8%BF%94%E5%9B%9E%E6%97%A0%E7%A9%B7%E5%A4%A7.html&quot;&gt;WPF 什么时候 VisualTreeHelper.GetDescendantBounds 将返回无穷大&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/wandia/p/17084881.html&quot;&gt;WrapPanel改进 - 老板娘的神秘商店 - 博客园&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-TranslatePoint-%E6%8D%A2%E7%AE%97%E5%85%83%E7%B4%A0%E4%B9%8B%E9%97%B4%E7%9B%B8%E5%AF%B9%E5%9D%90%E6%A0%87.html&quot;&gt;WPF 使用 TranslatePoint 换算元素之间相对坐标&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;xaml&quot;&gt;XAML&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/XAML-%E7%BB%99%E8%B5%84%E6%BA%90%E8%B5%B7%E4%B8%AA%E5%A5%BD%E5%90%8D%E5%AD%97-%E7%94%A8-StaticResource-%E8%B5%B7%E4%B8%80%E4%B8%AA%E5%88%AB%E5%90%8D.html&quot;&gt;XAML 给资源起个好名字 用 StaticResource 起一个别名&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%9C%A8-XAML-%E5%86%99-C-%E4%BB%A3%E7%A0%81.html&quot;&gt;WPF 在 XAML 写 C# 代码&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E9%80%9A%E8%BF%87%E8%BE%85%E5%8A%A9%E6%96%B9%E6%B3%95%E5%9C%A8-csharp-%E4%BB%A3%E7%A0%81%E5%86%99%E5%87%BA-XAML-%E7%95%8C%E9%9D%A2%E6%95%88%E6%9E%9C.html&quot;&gt;WPF 通过辅助方法在 csharp 代码写出 XAML 界面效果&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-Behavior-%E5%BA%93%E8%BE%85%E5%8A%A9%E8%8E%B7%E5%8F%96%E5%8A%A8%E6%80%81%E8%B5%84%E6%BA%90%E5%8F%98%E6%9B%B4%E4%BA%8B%E4%BB%B6.html&quot;&gt;WPF 使用 Behavior 库辅助获取动态资源变更事件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-ShowMeTheXAML-%E6%98%BE%E7%A4%BA-WPF-%E7%9A%84-XAML-%E6%8E%A7%E4%BB%B6%E5%86%85%E5%AE%B9.html&quot;&gt;dotnet 使用 ShowMeTheXAML 显示 WPF 的 XAML 控件内容&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-dotnet-core-%E7%9A%84-Blend-SDK-Behaviors-%E5%BA%93.html&quot;&gt;WPF dotnet core 的 Blend SDK Behaviors 库&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/xaml-%E6%B7%BB%E5%8A%A0-region.html&quot;&gt;xaml 添加 region&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%82%B9%E5%87%BB%E6%8C%89%E9%92%AE%E6%97%B6%E6%9B%B4%E6%94%B9%E6%8C%89%E9%92%AE%E6%A0%B7%E5%BC%8F%E7%95%8C%E9%9D%A2%E6%95%88%E6%9E%9C%E7%9A%84-XAML-%E5%AE%9E%E7%8E%B0%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 点击按钮时更改按钮样式界面效果的 XAML 实现方法&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;图片&quot;&gt;图片&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BB%8E%E6%96%87%E4%BB%B6%E5%88%9B%E5%BB%BA%E5%9B%BE%E7%89%87%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 从文件创建图片的方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BB%8E-DrawingVisual-%E8%BD%AC-BitmapImage-%E5%9B%BE%E7%89%87.html&quot;&gt;WPF 从 DrawingVisual 转 BitmapImage 图片&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BF%AE%E6%94%B9%E5%9B%BE%E7%89%87%E9%A2%9C%E8%89%B2.html&quot;&gt;WPF 修改图片颜色&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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&quot;&gt;WPF 通过位处理合并图片&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%9B%BE%E7%89%87%E7%A7%BB%E9%99%A4%E8%A7%86%E8%A7%89%E6%A0%91%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F.html&quot;&gt;WPF 图片移除视觉树内存泄漏&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/gif-%E6%A0%BC%E5%BC%8F.html&quot;&gt;gif 格式&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%B8%80%E4%B8%AA%E6%80%A7%E8%83%BD%E6%AF%94%E8%BE%83%E5%A5%BD%E7%9A%84-gif-%E8%A7%A3%E6%9E%90%E5%BA%93.html&quot;&gt;WPF 一个性能比较好的 gif 解析库&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/wpf-%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8-Magick.NET-%E6%92%AD%E6%94%BE-gif-%E5%9B%BE%E7%89%87.html&quot;&gt;wpf 如何使用 Magick.NET 播放 gif 图片&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/wpf-GifBitmapDecoder-%E8%A7%A3%E6%9E%90-gif-%E6%A0%BC%E5%BC%8F.html&quot;&gt;wpf GifBitmapDecoder 解析 gif 格式&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%92%AD%E6%94%BE-gif.html&quot;&gt;WPF 播放 gif&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E9%80%9A%E8%BF%87-GifBitmapDecoder-%E8%B0%83%E7%94%A8-WIC-%E8%A7%A3%E6%9E%90-Gif-%E5%92%8C%E8%BF%9B%E8%A1%8C%E5%8A%A8%E7%94%BB%E6%92%AD%E6%94%BE%E7%9A%84%E7%AE%80%E5%8D%95%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 通过 GifBitmapDecoder 调用 WIC 解析 Gif 和进行动画播放的简单方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%88%9B%E5%BB%BA%E7%A9%BA%E7%99%BD%E5%9B%BE%E7%89%87.html&quot;&gt;WPF 创建空白图片&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E9%80%9A%E8%BF%87-EXIF-%E8%AE%BE%E7%BD%AE%E5%92%8C%E8%AF%BB%E5%8F%96%E5%9B%BE%E7%89%87%E7%9A%84%E6%97%8B%E8%BD%AC%E4%BF%A1%E6%81%AF.html&quot;&gt;WPF 通过 EXIF 设置和读取图片的旋转信息&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E9%80%9A%E8%BF%87-WriteableBitmap-%E5%AE%9E%E7%8E%B0-TAGC-%E4%BD%8E%E5%85%89%E5%A2%9E%E5%BC%BA%E6%95%88%E6%9E%9C%E7%AE%97%E6%B3%95.html&quot;&gt;WPF 通过 WriteableBitmap 实现 TAGC 低光增强效果算法&lt;/a&gt;
&lt;!-- [WPF 通过 WriteableBitmap 实现 TAGC 低光增强效果算法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19096083 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;文本&quot;&gt;文本&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%96%87%E5%AD%97%E6%8F%8F%E8%BE%B9.html&quot;&gt;WPF 文字描边&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%BC%B9%E5%87%BA-popup-%E9%87%8C%E9%9D%A2%E7%9A%84-TextBox-%E6%97%A0%E6%B3%95%E8%BE%93%E5%85%A5%E6%B1%89%E5%AD%97.html&quot;&gt;WPF 弹出 popup 里面的 TextBox 无法输入汉字&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%8B%BC%E9%9F%B3%E8%BE%93%E5%85%A5%E6%B3%95.html&quot;&gt;WPF 拼音输入法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%8E%B7%E5%BE%97%E5%BD%93%E5%89%8D%E8%BE%93%E5%85%A5%E6%B3%95%E8%AF%AD%E8%A8%80%E5%8C%BA%E5%9F%9F.html&quot;&gt;WPF 获得当前输入法语言区域&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%AE%BE%E7%BD%AE%E8%BE%93%E5%85%A5%E5%8F%AA%E8%83%BD%E8%8B%B1%E6%96%87.html&quot;&gt;WPF 设置输入只能英文&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-TextBlock-%E4%B8%8A%E6%A0%87.html&quot;&gt;C＃ TextBlock 上标&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-OpenXML-WPF-%E8%A7%A3%E6%9E%90%E5%AE%9E%E7%8E%B0-PPT-%E6%96%87%E6%9C%AC%E6%8F%8F%E8%BE%B9%E6%95%88%E6%9E%9C.html&quot;&gt;dotnet OpenXML WPF 解析实现 PPT 文本描边效果&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post代码占用的空/dotnet-%E8%A7%A3%E6%9E%90-TTF-%E5%AD%97%E4%BD%93%E6%96%87%E4%BB%B6%E6%A0%BC%E5%BC%8F.html&quot;&gt;dotnet 解析 TTF 字体文件格式&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BB%8E%E6%96%87%E4%BB%B6%E5%8A%A0%E8%BD%BD%E5%AD%97%E4%BD%93.html&quot;&gt;WPF 从文件加载字体&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%AE%80%E5%8D%95%E8%81%8A%E8%81%8A%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8-DrawGlyphRun-%E7%BB%98%E5%88%B6%E6%96%87%E6%9C%AC.html&quot;&gt;WPF 简单聊聊如何使用 DrawGlyphRun 绘制文本&lt;/a&gt;
&lt;!-- [WPF 简单聊聊如何使用 DrawGlyphRun 绘制文本 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/15388005.html ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%B5%8B%E8%AF%95-GlyphTypeface-%E7%9A%84-Baseline-%E8%A1%8C%E4%B8%BA.html&quot;&gt;WPF 测试 GlyphTypeface 的 Baseline 行为&lt;/a&gt;
&lt;!-- [WPF 测试 GlyphTypeface 的 Baseline 行为 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18658843 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%8E%B7%E5%8F%96%E6%9C%AC%E6%9C%BA%E6%89%80%E6%9C%89%E5%AD%97%E4%BD%93%E6%8B%BF%E5%88%B0%E6%AF%8F%E4%B8%AA%E5%AD%97%E7%AC%A6%E7%9A%84%E5%AE%BD%E5%BA%A6%E5%92%8C%E9%AB%98%E5%BA%A6.html&quot;&gt;WPF 获取本机所有字体拿到每个字符的宽度和高度&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E4%BD%BF%E7%94%A8-FreeType-%E8%AF%BB%E5%8F%96%E5%92%8C%E7%BB%98%E5%88%B6%E5%AD%97%E4%BD%93.html&quot;&gt;dotnet C# 使用 FreeType 读取和绘制字体&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%87%AA%E5%AE%9A%E4%B9%89%E6%96%87%E6%9C%AC%E6%A1%86%E8%BE%93%E5%85%A5%E6%B3%95-IME-%E8%B7%9F%E9%9A%8F%E5%85%89%E6%A0%87.html&quot;&gt;WPF 自定义文本框输入法 IME 跟随光标&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E6%96%87%E6%9C%AC%E5%BA%93-%E8%81%8A%E8%81%8A%E8%A1%8C%E9%A6%96%E5%85%89%E6%A0%87%E7%9A%84%E8%A1%8C%E4%B8%BA.html&quot;&gt;文本库 聊聊行首光标的行为&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%A6%82%E4%BD%95%E6%AD%A3%E7%A1%AE%E8%8E%B7%E5%8F%96%E8%97%8F%E6%96%87%E7%9A%84%E5%AD%97%E6%95%B0.html&quot;&gt;dotnet C# 如何正确获取藏文的字数&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%AD%97%E4%BD%93-FontStyle-%E7%9A%84-Italic-%E5%92%8C-Oblique-%E7%9A%84%E5%8C%BA%E5%88%AB.html&quot;&gt;WPF 字体 FontStyle 的 Italic 和 Oblique 的区别&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%A7%A3%E5%86%B3-SelectionTextBrush-%E8%AE%BE%E7%BD%AE%E6%97%A0%E6%95%88%E9%97%AE%E9%A2%98.html&quot;&gt;WPF 解决 SelectionTextBrush 设置无效问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-How-to-get-plain-text-from-RichTextBox.html&quot;&gt;WPF How to get plain text from RichTextBox&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/wpf-PreviewTextInput-%E5%9C%A8%E9%BC%A0%E6%A0%87%E8%BE%93%E5%85%A5%E8%8E%B7%E5%BE%97-_u0003.html&quot;&gt;wpf PreviewTextInput 在鼠标输入获得 \u0003&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%8E%A2%E7%B4%A2-Skia-%E7%9A%84%E7%AB%96%E6%8E%92%E6%96%87%E6%9C%AC%E6%B8%B2%E6%9F%93%E7%9A%84%E5%AD%97%E7%AC%A6%E9%AB%98%E5%BA%A6.html&quot;&gt;WPF 探索 Skia 的竖排文本渲染的字符高度&lt;/a&gt;
&lt;!-- [WPF 探索 Skia 的竖排文本渲染的字符高度 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18815810 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;线程&quot;&gt;线程&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%A6%82%E4%BD%95%E8%B7%A8%E7%BA%BF%E7%A8%8B%E9%87%8D%E6%96%B0%E6%8A%9B%E5%87%BA%E5%BC%82%E5%B8%B8.html&quot;&gt;WPF 如何跨线程重新抛出异常&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%AE%BE%E7%BD%AE%E8%B5%84%E6%BA%90%E5%AD%97%E5%85%B8%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E8%AF%BB%E5%86%99%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 设置资源字典多线程安全读写方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%AE%80%E5%8D%95%E5%88%A4%E6%96%AD%E4%B8%BB%E7%BA%BF%E7%A8%8B%E7%95%8C%E9%9D%A2%E6%98%AF%E5%90%A6%E5%8D%A1%E9%A1%BF%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 简单判断主线程界面是否卡顿的方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%A4%9A%E7%BA%BF%E7%A8%8B%E4%B8%8B%E8%B7%A8%E7%BA%BF%E7%A8%8B%E5%A4%84%E7%90%86-ObservableCollection-%E6%95%B0%E6%8D%AE.html&quot;&gt;WPF 多线程下跨线程处理 ObservableCollection 数据&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%B7%A8%E7%BA%BF%E7%A8%8B-UI-%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 跨线程 UI 的方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/wpf-%E4%BD%BF%E7%94%A8-Dispatcher.Invoke-%E5%86%BB%E7%BB%93%E7%AA%97%E5%8F%A3.html&quot;&gt;wpf 使用 Dispatcher.Invoke 冻结窗口&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-Dispatcher-%E7%9A%84-InvokeAsync-%E5%92%8C-BeginInvoke-%E7%9A%84%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%E5%B7%AE%E5%88%AB.html&quot;&gt;WPF 使用 Dispatcher 的 InvokeAsync 和 BeginInvoke 的异常处理差别&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;窗口&quot;&gt;窗口&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%B8%80%E4%B8%AA%E7%A9%BA%E7%9A%84-WPF-%E7%A8%8B%E5%BA%8F%E6%9C%89%E5%A4%9A%E5%B0%91%E4%B8%AA%E7%AA%97%E5%8F%A3.html&quot;&gt;WPF 一个空的 WPF 程序有多少个窗口&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%86%85%E9%83%A8%E7%9A%845%E4%B8%AA%E7%AA%97%E5%8F%A3%E4%B9%8B-MediaContextNotificationWindow.html&quot;&gt;WPF 内部的5个窗口之 MediaContextNotificationWindow&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%8E%B7%E5%8F%96%E5%BA%94%E7%94%A8%E7%9A%84%E6%89%80%E6%9C%89%E7%AA%97%E5%8F%A3.html&quot;&gt;WPF 获取应用的所有窗口&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%A7%A3%E5%86%B3%E5%BC%B9%E5%87%BA%E6%A8%A1%E6%80%81%E7%AA%97%E5%8F%A3%E5%85%B3%E9%97%AD%E5%90%8E-%E4%B8%BB%E7%AA%97%E5%8F%A3%E4%B8%8D%E5%9C%A8%E6%9C%80%E5%89%8D.html&quot;&gt;WPF 解决弹出模态窗口关闭后，主窗口不在最前&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%AE%A9%E7%AA%97%E5%8F%A3%E6%BF%80%E6%B4%BB%E4%BD%9C%E4%B8%BA%E5%89%8D%E5%8F%B0%E6%9C%80%E4%B8%8A%E5%B1%82%E7%AA%97%E5%8F%A3%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 让窗口激活作为前台最上层窗口的方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%B8%A4%E4%B8%AA-Topmost-%E7%9A%84%E7%AA%97%E5%8F%A3%E5%A6%82%E4%BD%95%E8%AE%BE%E7%BD%AE%E8%B0%81%E5%9C%A8%E6%9C%80%E4%B8%8A%E6%96%B9.html&quot;&gt;WPF 两个 Topmost 的窗口如何设置谁在最上方&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%8E%B7%E5%8F%96%E6%9F%90%E4%B8%AA%E7%AA%97%E5%8F%A3%E7%9A%84%E6%89%80%E6%9C%89%E5%AD%90%E7%AA%97%E5%8F%A3.html&quot;&gt;WPF 获取某个窗口的所有子窗口&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%AE%BE%E7%BD%AE-ShowInTaskbar-%E5%AF%B9%E7%AA%97%E5%8F%A3%E6%9C%80%E5%B0%8F%E5%8C%96%E7%9A%84%E5%BD%B1%E5%93%8D.html&quot;&gt;WPF 设置 ShowInTaskbar 对窗口最小化的影响&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;

&lt;p&gt;&lt;a href=&quot;/post/Win32-%E4%BD%BF%E7%94%A8-SetCurrentProcessExplicitAppUserModelID-%E5%85%B3%E8%81%94%E5%A4%9A%E4%B8%AA%E8%BF%9B%E7%A8%8B-%E5%9C%A8%E4%BB%BB%E5%8A%A1%E6%A0%8F%E5%90%88%E5%B9%B6-WPF-%E5%A4%9A%E8%BF%9B%E7%A8%8B%E7%AA%97%E5%8F%A3.html&quot;&gt;Win32 使用 SetCurrentProcessExplicitAppUserModelID 关联多个进程 在任务栏合并 WPF 多进程窗口&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%A8%B3%E5%AE%9A%E7%9A%84%E5%85%A8%E5%B1%8F%E5%8C%96%E7%AA%97%E5%8F%A3%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 稳定的全屏化窗口方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E7%94%A8-SetWindowPos-%E6%96%B9%E6%B3%95%E8%AE%BE%E7%BD%AE%E4%B8%80%E4%B8%AA%E5%81%9C%E6%AD%A2%E5%93%8D%E5%BA%94%E7%9A%84%E7%AA%97%E5%8F%A3%E5%B0%86%E5%8D%A1%E8%B0%83%E7%94%A8%E6%96%B9.html&quot;&gt;用 SetWindowPos 方法设置一个停止响应的窗口将卡调用方&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%85%A8%E5%B1%8F%E9%80%8F%E6%98%8E%E7%AA%97%E5%8F%A3.html&quot;&gt;WPF 全屏透明窗口&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%B7%BB%E5%8A%A0%E7%AA%97%E5%8F%A3%E6%B6%88%E6%81%AF%E9%92%A9%E5%AD%90%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 添加窗口消息钩子方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%9C%A8-Alt+Tab-%E9%9A%90%E8%97%8F%E7%AA%97%E5%8F%A3.html&quot;&gt;WPF 在 Alt+Tab 隐藏窗口&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%8E%B7%E5%8F%96%E5%85%A8%E5%B1%80%E6%89%80%E6%9C%89%E7%AA%97%E5%8F%A3%E7%9A%84%E5%88%9B%E5%BB%BA%E6%98%BE%E7%A4%BA%E4%BA%8B%E4%BB%B6-%E7%9B%91%E6%8E%A7%E7%AA%97%E5%8F%A3%E6%89%93%E5%BC%80.html&quot;&gt;WPF 获取全局所有窗口的创建显示事件 监控窗口打开&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E9%80%9A%E8%BF%87-SetWindowDisplayAffinity-%E9%85%8D%E7%BD%AE%E7%A6%81%E6%AD%A2%E5%AF%B9%E7%AA%97%E5%8F%A3%E8%BF%9B%E8%A1%8C%E6%88%AA%E5%9B%BE%E6%88%96%E5%BD%95%E5%B1%8F.html&quot;&gt;WPF 通过 SetWindowDisplayAffinity 配置禁止对窗口进行截图或录屏&lt;/a&gt;
&lt;!-- [WPF 通过 SetWindowDisplayAffinity 配置禁止对窗口进行截图或录屏 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18250172 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/dino623/p/custom_window_style_using_WindowChrome.html&quot;&gt;[WPF 自定义控件] 使用WindowChrome自定义Window Style - dino.c - 博客园&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.walterlv.com/post/wpf-simulate-native-window-style-using-window-chrome.html&quot;&gt;WPF 使用 WindowChrome，在自定义窗口标题栏的同时最大程度保留原生窗口样式（类似 UWP/Chrome） - walterlv&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/jasongrass/p/18555472&quot;&gt;将 WPF 嵌入到 MFC 中，无法响应键盘输入 - J.晒太阳的猫 - 博客园&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;渲染&quot;&gt;渲染&lt;/h3&gt;

&lt;p&gt;更多请看 &lt;a href=&quot;/post/%E5%8D%9A%E5%AE%A2%E5%AF%BC%E8%88%AA.html#wpf-%E6%B8%B2%E6%9F%93%E7%9B%B8%E5%85%B3&quot;&gt;WPF 渲染相关&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%A6%82%E4%BD%95%E7%94%BB%E5%87%BA1%E5%83%8F%E7%B4%A0%E7%9A%84%E7%BA%BF.html&quot;&gt;WPF 如何画出1像素的线&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%A7%A3%E5%86%B3-ViewBox-%E4%B8%8D%E6%98%BE%E7%A4%BA%E7%BA%BF%E7%9A%84%E9%97%AE%E9%A2%98.html&quot;&gt;WPF 解决 ViewBox 不显示线的问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E9%80%9A%E8%BF%87-DrawingContext-DrawImage-%E7%BB%98%E5%88%B6%E5%9B%BE%E7%89%87.html&quot;&gt;WPF 通过 DrawingContext DrawImage 绘制图片&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%9F%BA%E7%A1%80%E7%BB%98%E5%9B%BE-%E5%88%9B%E5%BB%BA%E5%92%8C%E5%8A%A0%E5%B7%A5%E5%9B%BE%E7%89%87.html&quot;&gt;WPF 基础绘图 创建和加工图片&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%A6%82%E4%BD%95%E5%9C%A8-WriteableBitmap-%E5%86%99%E6%96%87%E5%AD%97.html&quot;&gt;WPF 如何在 WriteableBitmap 写文字&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%A6%82%E4%BD%95%E7%BB%99%E5%AE%9A%E4%B8%A4%E4%B8%AA%E7%82%B9%E7%94%BB%E5%87%BA%E4%B8%80%E6%9D%A1%E6%B3%A2%E6%B5%AA%E7%BA%BF.html&quot;&gt;WPF 如何给定两个点画出一条波浪线&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-DrawingVisual.html&quot;&gt;WPF DrawingVisual&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%A6%82%E4%BD%95%E5%88%A4%E6%96%AD%E4%B8%A4%E4%B8%AA-LinearGradientBrush-%E7%9B%B8%E7%AD%89.html&quot;&gt;WPF 如何判断两个 LinearGradientBrush 相等&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%9C%A8-DrawingContext-%E7%9A%84-push-%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8.html&quot;&gt;WPF 在 DrawingContext 的 push 如何使用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%AE%BE%E7%BD%AE%E7%BA%AF%E8%BD%AF%E4%BB%B6%E6%B8%B2%E6%9F%93.html&quot;&gt;WPF 设置纯软件渲染&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%AC%94%E5%88%B7%E7%BB%91%E5%AE%9A%E4%B8%8D%E4%B8%8A%E5%8F%AF%E8%83%BD%E7%9A%84%E5%8E%9F%E5%9B%A0.html&quot;&gt;WPF 笔刷绑定不上可能的原因&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%B0%83%E7%94%A8-InvalidateVisual-%E4%B8%8D%E8%A7%A6%E5%8F%91-OnRender-%E7%9A%84%E5%8E%9F%E5%9B%A0.html&quot;&gt;WPF 调用 InvalidateVisual 不触发 OnRender 的原因&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%A7%A3%E5%86%B3-Skia-%E5%9B%A0%E4%B8%BA%E6%89%BE%E4%B8%8D%E5%88%B0%E5%AD%97%E4%BD%93%E8%80%8C%E7%BB%98%E5%88%B6%E4%B8%8D%E5%87%BA%E4%B8%AD%E6%96%87%E5%AD%97%E7%AC%A6.html&quot;&gt;WPF 解决 Skia 因为找不到字体而绘制不出中文字符&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-Skia-%E7%BB%98%E5%88%B6-WriteableBitmap-%E5%9B%BE%E7%89%87.html&quot;&gt;WPF 使用 Skia 绘制 WriteableBitmap 图片&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%A6%82%E4%BD%95%E8%8E%B7%E5%8F%96%E6%9C%89%E5%93%AA%E4%BA%9B-VisualBrush-%E7%94%A8%E4%BA%86%E6%9F%90%E4%B8%AA%E6%8E%A7%E4%BB%B6.html&quot;&gt;WPF 如何获取有哪些 VisualBrush 用了某个控件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%9C%A8-WPF-%E9%87%8C%E6%98%BE%E7%A4%BA%E6%95%B0%E5%AD%A6-%CF%80-%E7%9A%84%E9%A2%9C%E8%89%B2.html&quot;&gt;dotnet 在 WPF 里显示数学 π 的颜色&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%9B%B4%E6%94%B9-DrawingVisual-%E7%9A%84-RenderOpen-%E7%94%A8%E5%88%B0%E7%9A%84%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%86%85%E5%AE%B9%E5%B0%86%E6%8C%81%E7%BB%AD%E5%BD%B1%E5%93%8D%E6%B8%B2%E6%9F%93%E6%95%88%E6%9E%9C.html&quot;&gt;WPF 更改 DrawingVisual 的 RenderOpen 用到的对象的内容将持续影响渲染效果&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%AE%80%E5%8D%95%E8%81%8A%E8%81%8A%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8-DrawGlyphRun-%E7%BB%98%E5%88%B6%E6%96%87%E6%9C%AC.html&quot;&gt;WPF 简单聊聊如何使用 DrawGlyphRun 绘制文本&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%A8%A1%E6%8B%9F-WPFMediaKit-%E7%9A%84-D3D-%E9%85%8D%E7%BD%AE%E7%94%A8%E6%9D%A5%E6%B5%8B%E8%AF%954k%E6%80%A7%E8%83%BD.html&quot;&gt;WPF 模拟 WPFMediaKit 的 D3D 配置用来测试4k性能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-Direct-Manipulation-%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 使用 Direct Manipulation 的方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-Composition-API-%E5%81%9A%E9%AB%98%E6%80%A7%E8%83%BD%E6%B8%B2%E6%9F%93.html&quot;&gt;WPF 使用 Composition API 做高性能渲染&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%B8%B2%E6%9F%93%E7%BA%A7%E5%88%AB.html&quot;&gt;WPF 渲染级别&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-Win2d-%E6%B8%B2%E6%9F%93.html&quot;&gt;WPF 使用 Win2d 渲染&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%8A%A8%E7%94%BB%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E5%BA%94%E7%94%A8-%E4%B8%80%E5%8D%83%E4%B8%AA%E5%8D%8A%E9%80%8F%E6%98%8E%E7%9F%A9%E5%BD%A2%E5%81%9A%E5%8A%A8%E7%94%BB.html&quot;&gt;WPF 动画性能测试应用 一千个半透明矩形做动画&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%8E%B7%E5%8F%96-MediaContext-%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 获取 MediaContext 的方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%AE%BE%E7%BD%AE-Aliased-%E7%9A%84-EdgeMode-%E4%BC%9A%E8%AE%A9%E5%9C%86%E5%BD%A2%E6%B8%B2%E6%9F%93%E5%87%BA%E6%A3%B1%E8%A7%92.html&quot;&gt;WPF 设置 Aliased 的 EdgeMode 会让圆形渲染出棱角&lt;/a&gt;
&lt;!-- [WPF 设置 Aliased 的 EdgeMode 会让圆形渲染出棱角 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18905146 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-Windows-%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8-DwmFlush-%E5%AF%B9%E9%BD%90%E5%88%B7%E6%96%B0%E7%8E%87.html&quot;&gt;dotnet C# Windows 桌面应用程序简单使用 DwmFlush 对齐刷新率&lt;/a&gt;
&lt;!-- [dotnet C# Windows 桌面应用程序简单使用 DwmFlush 对齐刷新率 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18932218 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;视频&quot;&gt;视频&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-VideoDrawing-%E6%92%AD%E6%94%BE%E8%A7%86%E9%A2%91.html&quot;&gt;WPF 使用 VideoDrawing 播放视频&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/xymfblogs/archive/2022/12/15/16983553.html&quot;&gt;[WPF] MediaElement播放HDR视频泛黄、颜色显示不正确应该如何解决？ - 王_先_生 - 博客园&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;动画&quot;&gt;动画&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%90%8E%E5%8F%B0%E4%BB%A3%E7%A0%81%E5%81%9A-TranslateTransform-%E7%9A%84%E5%8A%A8%E7%94%BB.html&quot;&gt;WPF 后台代码做 TranslateTransform 的动画&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%8A%A8%E7%94%BB%E5%AE%9E%E6%88%98-%E7%82%B9%E5%87%BB%E6%97%B6%E6%98%BE%E7%A4%BA%E5%9C%86%E5%9C%88%E6%B7%A1%E5%87%BA%E6%95%88%E6%9E%9C.html&quot;&gt;WPF 动画实战 点击时显示圆圈淡出效果&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;平台调用-1&quot;&gt;平台调用&lt;/h3&gt;

&lt;h4 id=&quot;win32&quot;&gt;Win32&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%89%93%E5%BC%80%E8%B5%84%E6%BA%90%E7%AE%A1%E7%90%86%E5%99%A8%E4%B8%94%E9%80%89%E4%B8%AD%E6%9F%90%E4%B8%AA%E6%96%87%E4%BB%B6.html&quot;&gt;WPF 打开资源管理器且选中某个文件&lt;/a&gt;
&lt;!-- [WPF 打开资源管理器且选中某个文件 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18548977 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%8E%B7%E5%8F%96%E5%B1%8F%E5%B9%95%E6%9F%90%E4%B8%AA%E7%82%B9%E7%9A%84%E9%A2%9C%E8%89%B2.html&quot;&gt;WPF 获取屏幕某个点的颜色&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BF%AE%E6%94%B9%E5%B1%8F%E5%B9%95%E4%BA%AE%E5%BA%A6.html&quot;&gt;WPF 修改屏幕亮度&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-RawInput-%E6%8E%A5%E6%94%B6%E8%A3%B8%E6%95%B0%E6%8D%AE.html&quot;&gt;WPF 使用 RawInput 接收裸数据&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%8E%A2%E7%B4%A2%E4%BB%BB%E5%8A%A1%E7%AE%A1%E7%90%86%E5%99%A8%E7%9A%84%E8%BF%9B%E7%A8%8B%E5%88%86%E7%BB%84%E9%80%BB%E8%BE%91.html&quot;&gt;WPF 探索任务管理器的进程分组逻辑&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-DisplayConfigGetDeviceInfo-%E8%8E%B7%E5%8F%96%E6%98%BE%E7%A4%BA%E5%99%A8%E5%90%8D%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 使用 DisplayConfigGetDeviceInfo 获取显示器名的方法&lt;/a&gt;
&lt;!-- [WPF 使用 DisplayConfigGetDeviceInfo 获取显示器名的方法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19333992 ) --&gt;&lt;/p&gt;

&lt;h4 id=&quot;winrt&quot;&gt;WinRT&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%B8%8D%E5%AE%89%E8%A3%85-WindowsAppSDK-%E4%BD%BF%E7%94%A8-WinRT-%E5%8A%9F%E8%83%BD%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 不安装 WindowsAppSDK 使用 WinRT 功能的方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BF%AE%E5%A4%8D-WPF-%E5%AE%89%E8%A3%85-WindowsAppSDK-%E5%BA%93%E6%9E%84%E5%BB%BA%E5%A4%B1%E8%B4%A5-NETSDK1082-%E5%92%8C-NETSDK1112-%E6%89%BE%E4%B8%8D%E5%88%B0-win10-arm-%E5%A4%B1%E8%B4%A5.html&quot;&gt;修复 WPF 安装 WindowsAppSDK 库构建失败 NETSDK1082 和 NETSDK1112 找不到 win10-arm 失败&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-Win10-%E7%9A%84-WinRT-%E8%87%AA%E5%B8%A6-Windows.Media.Ocr-%E5%AE%9E%E7%8E%B0%E5%9B%BE%E7%89%87%E8%BD%AC%E6%96%87%E6%9C%AC.html&quot;&gt;WPF 使用 Win10 的 WinRT 自带 Windows.Media.Ocr 实现图片转文本&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E9%80%9A%E8%BF%87-WindowsAppSDK-%E4%BD%BF%E7%94%A8-WinRT-%E7%9A%84%E6%89%8B%E5%86%99%E8%AF%86%E5%88%AB%E5%8A%9F%E8%83%BD.html&quot;&gt;WPF 通过 WindowsAppSDK 使用 WinRT 的手写识别功能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/UWP-WinRT-%E4%BD%BF%E7%94%A8%E7%B3%BB%E7%BB%9F%E8%87%AA%E5%B8%A6%E7%9A%84%E5%88%86%E8%AF%8D%E5%BA%93%E5%AF%B9%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%96%87%E6%9C%AC%E8%BF%9B%E8%A1%8C%E5%88%86%E8%AF%8D.html&quot;&gt;UWP WinRT 使用系统自带的分词库对字符串文本进行分词&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%B8%8D%E5%B8%A6-TargetPlatformVersion-%E6%98%BE%E7%A4%BA-Win10-%E7%9A%84-Toast-%E9%80%9A%E7%9F%A5%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 不带 TargetPlatformVersion 显示 Win10 的 Toast 通知的方法&lt;/a&gt;
&lt;!-- [WPF 不带 TargetPlatformVersion 显示 Win10 的 Toast 通知的方法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18333724 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;wpf-基础-2d-图形学知识&quot;&gt;WPF 基础 2D 图形学知识&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%9F%BA%E7%A1%80-2D-%E5%9B%BE%E5%BD%A2%E5%AD%A6%E7%9F%A5%E8%AF%86.html&quot;&gt;WPF 基础 2D 图形学知识&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%9F%BA%E7%A1%80-2D-%E5%9B%BE%E5%BD%A2%E5%AD%A6%E7%9F%A5%E8%AF%86-%E5%88%A4%E6%96%AD%E7%82%B9%E6%98%AF%E5%90%A6%E5%9C%A8%E7%BA%BF%E6%AE%B5%E4%B8%8A.html&quot;&gt;WPF 基础 2D 图形学知识 判断点是否在线段上&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%9F%BA%E7%A1%80-2D-%E5%9B%BE%E5%BD%A2%E5%AD%A6%E7%9F%A5%E8%AF%86-%E5%88%A4%E6%96%AD%E7%82%B9%E6%98%AF%E5%90%A6%E5%9C%A8%E4%BB%BB%E6%84%8F%E5%87%A0%E4%BD%95%E5%86%85%E9%83%A8%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 基础 2D 图形学知识 判断点是否在任意几何内部方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%9F%BA%E7%A1%80-2D-%E5%9B%BE%E5%BD%A2%E5%AD%A6%E7%9F%A5%E8%AF%86-%E6%B1%82%E5%90%91%E9%87%8F%E6%97%8B%E8%BD%AC%E8%A7%92%E5%BA%A6.html&quot;&gt;WPF 基础 2D 图形学知识 求向量旋转角度&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E5%88%A4%E6%96%AD%E4%B8%A4%E6%9D%A1%E7%9B%B4%E7%BA%BF%E8%B7%9D%E7%A6%BB.html&quot;&gt;C# 判断两条直线距离&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E5%B7%B2%E7%9F%A5%E7%82%B9%E5%92%8C%E5%90%91%E9%87%8F-%E6%B1%82%E8%B7%9D%E7%A6%BB%E7%9A%84%E7%82%B9.html&quot;&gt;C# 已知点和向量，求距离的点&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%A6%82%E4%BD%95%E8%AE%A1%E7%AE%97%E7%9F%A9%E5%BD%A2%E5%86%85%E4%B8%80%E4%B8%AA%E5%9D%90%E6%A0%87%E7%9B%B8%E5%AF%B9%E5%8F%A6%E4%B8%80%E4%B8%AA%E7%9F%A9%E5%BD%A2%E7%9A%84%E5%9D%90%E6%A0%87.html&quot;&gt;WPF 如何计算矩形内一个坐标相对另一个矩形的坐标&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;wpf-已知问题&quot;&gt;WPF 已知问题&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E7%AA%97%E5%8F%A3%E5%9C%A8%E5%B1%8F%E5%B9%95%E5%A4%96%E5%88%9B%E5%BB%BA%E5%B0%86%E4%B8%8D%E4%BC%9A%E5%88%B7%E6%96%B0%E6%B8%B2%E6%9F%93.html&quot;&gt;WPF 已知问题 窗口在屏幕外创建将不会刷新渲染&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E5%85%A8%E5%B1%8F%E9%80%8F%E6%98%8E%E7%AA%97%E5%8F%A3%E5%BC%B9%E5%87%BA%E5%AD%90%E7%AA%97%E5%8F%A3%E4%BC%9A%E9%97%AA%E7%83%81.html&quot;&gt;WPF 已知问题 全屏透明窗口弹出子窗口会闪烁&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-BitmapDecoder.Create-%E4%B8%8D%E6%94%AF%E6%8C%81%E4%BC%A0%E5%85%A5-Asynchronous-%E7%9A%84%E6%96%87%E4%BB%B6%E6%B5%81.html&quot;&gt;WPF 已知问题 BitmapDecoder.Create 不支持传入 Asynchronous 的文件流&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/wpf-VisualBrush-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98.html&quot;&gt;wpf VisualBrush 已知问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-Popup-%E5%90%83%E6%8E%89-PreviewMouseDown-%E4%BA%8B%E4%BB%B6.html&quot;&gt;WPF 已知问题 Popup 吃掉 PreviewMouseDown 事件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-Popup-%E5%A4%B1%E7%84%A6%E5%90%8E%E5%AF%BC%E8%87%B4-ListBox-%E6%97%A0%E6%B3%95%E7%94%A8-MouseWheel-%E6%BB%9A%E5%8A%A8%E9%97%AE%E9%A2%98%E5%92%8C%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 已知问题 Popup 失焦后导致 ListBox 无法用 MouseWheel 滚动问题和解决方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E8%B5%84%E6%BA%90%E5%AD%97%E5%85%B8%E6%A0%91%E5%BC%95%E7%94%A8%E4%B8%8E%E8%B5%84%E6%BA%90%E5%AF%BB%E6%89%BE%E7%9A%84%E5%9D%91.html&quot;&gt;WPF 已知问题 资源字典树引用与资源寻找的坑&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B0%86-StaticResource-%E5%92%8C-ResourceDictionary-%E6%94%BE%E5%9C%A8%E4%B8%80%E8%B5%B7%E7%9A%84%E9%AD%94%E5%B9%BB%E8%A1%8C%E4%B8%BA.html&quot;&gt;WPF 将 StaticResource 和 ResourceDictionary 放在一起的魔幻行为&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%8A%A0%E8%BD%BD%E8%AF%A1%E5%BC%82%E7%9A%84%E5%AD%97%E4%BD%93%E6%97%A0%E6%B3%95%E5%B8%83%E5%B1%80.html&quot;&gt;WPF 加载诡异的字体无法布局&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%BB%99-Pen-%E7%9A%84-DashStyle-%E8%AE%BE%E7%BD%AE-0-0-%E7%9A%84%E8%99%9A%E7%BA%BF%E6%95%B0%E7%BB%84%E5%B0%86%E4%BC%9A%E8%AE%A9%E6%B8%B2%E6%9F%93%E7%BA%BF%E7%A8%8B%E6%B6%88%E8%80%97%E5%A4%A7%E9%87%8F-CPU-%E8%B5%84%E6%BA%90.html&quot;&gt;WPF 给 Pen 的 DashStyle 设置 0 0 的虚线数组将会让渲染线程消耗大量 CPU 资源&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E5%8C%85%E5%90%AB-NaN-%E7%9A%84-Geometry-%E5%87%A0%E4%BD%95%E5%8F%AF%E8%83%BD%E5%AF%BC%E8%87%B4%E6%B8%B2%E6%9F%93%E5%B1%82%E6%8A%9B%E5%87%BA-UCEERR_RENDERTHREADFAILURE-%E5%BC%82%E5%B8%B8.html&quot;&gt;WPF 已知问题 包含 NaN 的 Geometry 几何可能导致渲染层抛出 UCEERR_RENDERTHREADFAILURE 异常&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E6%9F%90%E4%BA%9B%E8%AE%BE%E5%A4%87%E4%B8%8A%E7%9A%84%E5%BA%94%E7%94%A8%E5%9C%A8-WindowChromeWorker-%E6%8A%9B%E5%87%BA-System.OverflowException-%E5%BC%82%E5%B8%B8.html&quot;&gt;WPF 已知问题 某些设备上的应用在 WindowChromeWorker 抛出 System.OverflowException 异常&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%BA%94%E7%94%A8%E5%90%AF%E5%8A%A8%E8%BF%87%E7%A8%8B%E5%90%8C%E6%97%B6%E5%90%AF%E5%8A%A8%E5%A4%9A%E4%B8%AA-UI-%E7%BA%BF%E7%A8%8B%E4%B8%94%E8%AE%BF%E9%97%AE-ContentPresenter-%E5%8F%AF%E8%83%BD%E8%AE%A9%E5%A4%9A%E4%B8%AA-UI-%E7%BA%BF%E7%A8%8B%E4%BA%92%E7%AD%89.html&quot;&gt;WPF 应用启动过程同时启动多个 UI 线程且访问 ContentPresenter 可能让多个 UI 线程互等&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%B1%BB%E5%9E%8B%E7%9A%84%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E6%89%A7%E8%A1%8C%E7%AC%A6%E5%90%88%E6%8C%87%E5%AE%9A%E7%9A%84%E7%BB%91%E5%AE%9A%E7%BA%A6%E6%9D%9F%E7%9A%84%E8%B0%83%E7%94%A8%E6%97%B6%E5%BC%95%E5%8F%91%E4%BA%86%E5%BC%82%E5%B8%B8.html&quot;&gt;WPF 类型的构造函数执行符合指定的绑定约束的调用时引发了异常&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win7-%E6%97%A0%E6%B3%95%E5%90%AF%E5%8A%A8-WPF-%E7%A8%8B%E5%BA%8F-D3Dcompiler_47.dll-%E4%B8%A2%E5%A4%B1.html&quot;&gt;win7 无法启动 WPF 程序 D3Dcompiler_47.dll 丢失&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-RenderTargetBitmap-%E5%BF%AB%E9%80%9F%E6%88%AA%E5%9B%BE%E5%87%BA%E7%8E%B0-COMException-%E6%8F%90%E7%A4%BA.html&quot;&gt;WPF 使用 RenderTargetBitmap 快速截图出现 COMException 提示&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%96%B0%E5%BB%BA%E7%94%A8%E6%88%B7%E6%8E%A7%E4%BB%B6%E6%8F%90%E7%A4%BA-Error-MC3000-%E9%A6%96%E4%B8%AA-xaml-%E5%AD%97%E7%AC%A6%E4%B8%8D%E5%90%88%E6%B3%95.html&quot;&gt;WPF 新建用户控件提示 Error MC3000 首个 xaml 字符不合法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%9C%A8%E7%AA%97%E5%8F%A3%E7%9A%84-Deactivated-%E4%BD%BF%E7%94%A8-Mouse-%E7%9A%84-Capture-%E5%B0%86%E4%BC%9A%E8%AE%A9%E8%BF%9B%E7%A8%8B%E5%A4%B1%E5%8E%BB%E4%BA%A4%E4%BA%92.html&quot;&gt;WPF 在窗口的 Deactivated 使用 Mouse 的 Capture 将会让进程失去交互&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B0%86%E6%8E%A7%E4%BB%B6%E6%94%BE%E5%85%A5%E5%88%B0-UserControl-%E9%87%8C%E8%8E%B7%E5%8F%96-HwndSource-%E4%B8%BA%E7%A9%BA%E7%9A%84%E6%83%85%E5%86%B5.html&quot;&gt;WPF 将控件放入到 UserControl 里获取 HwndSource 为空的情况&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%A7%A3%E5%86%B3-PathTooLongException-%E8%B7%AF%E5%BE%84%E5%A4%AA%E9%95%BF.html&quot;&gt;WPF 解决 PathTooLongException 路径太长&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%AE%BE%E7%BD%AE-WS_EX_TRANSPARENT-%E8%A7%A6%E6%91%B8%E5%A4%B1%E6%95%88.html&quot;&gt;WPF 设置 WS_EX_TRANSPARENT 触摸失效&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%A7%A3%E5%86%B3-StylusPlugIn-%E7%82%B9%E5%87%BB%E7%A9%BF%E9%80%8F%E9%97%AE%E9%A2%98.html&quot;&gt;WPF 解决 StylusPlugIn 点击穿透问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E5%BC%80%E5%90%AF-WM_Pointer-%E6%B6%88%E6%81%AF%E4%B9%8B%E5%90%8E-%E8%8E%B7%E5%8F%96%E5%89%AF%E5%B1%8F%E8%A7%A6%E6%91%B8%E6%95%B0%E6%8D%AE%E5%9D%90%E6%A0%87%E5%81%8F%E7%A7%BB.html&quot;&gt;WPF 已知问题 开启 WM_Pointer 消息之后 获取副屏触摸数据坐标偏移&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%AD%A6%E6%83%95-StylusPlugIn-%E7%9A%84%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E9%97%AE%E9%A2%98.html&quot;&gt;WPF 警惕 StylusPlugIn 的多线程安全问题&lt;/a&gt;
&lt;!-- [WPF 警惕 StylusPlugIn 的多线程安全问题 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19085065 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%BD%91%E7%BB%9C-request-%E7%9A%84-read-%E6%96%B9%E6%B3%95%E4%B8%8D%E4%BC%9A%E8%BF%94%E5%9B%9E.html&quot;&gt;WPF 网络 request 的 read 方法不会返回&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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.html&quot;&gt;WPF 光标初始化的时候 temp 文件夹满了无法创建&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%8B%96%E5%8A%A8%E6%97%B6%E5%87%BA%E7%8E%B0-Invalid-FORMATETC-structure.html&quot;&gt;WPF 拖动时出现 Invalid FORMATETC structure&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-Process.Start-%E5%87%BA%E7%8E%B0-Win32Exception-%E5%BC%82%E5%B8%B8.html&quot;&gt;WPF Process.Start 出现 Win32Exception 异常&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-InputEventArgs-%E7%9A%84-Timestamp-%E5%B1%9E%E6%80%A7%E6%98%AF%E9%9D%99%E6%80%81%E7%9A%84%E5%AF%BC%E8%87%B4%E4%BA%8B%E4%BB%B6%E4%B9%8B%E9%97%B4%E7%9B%B8%E4%BA%92%E5%BD%B1%E5%93%8D.html&quot;&gt;WPF 已知问题 InputEventArgs 的 Timestamp 属性是静态的导致事件之间相互影响&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-Frame-%E7%9A%84-DataContext-%E4%B8%8D%E8%83%BD%E8%A2%AB-Page-%E7%BB%A7%E6%89%BF.html&quot;&gt;WPF Frame 的 DataContext 不能被 Page 继承&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-DelegateCommand-%E5%87%BA%E7%8E%B0Specified-cast-is-not-valid.html&quot;&gt;WPF DelegateCommand 出现Specified cast is not valid&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-VisualBrush-%E5%9C%A8-4k-%E5%8A%A0-200-DPI-%E8%AE%BE%E5%A4%87%E4%B8%8A%E6%9F%90%E4%BA%9B%E6%96%87%E6%9C%AC%E4%B8%8D%E6%B8%B2%E6%9F%93%E7%9C%8B%E4%B8%8D%E8%A7%81%E9%97%AE%E9%A2%98.html&quot;&gt;WPF 使用 VisualBrush 在 4k 加 200 DPI 设备上某些文本不渲染看不见问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AE%B0-Win8.1-%E6%9F%90%E5%BA%94%E7%94%A8%E6%B8%B2%E6%9F%93%E6%8A%9B%E5%87%BA-OutOfMemoryException-%E5%BC%82%E5%B8%B8%E5%8F%8A%E4%BF%AE%E5%A4%8D%E6%96%B9%E6%B3%95.html&quot;&gt;记 Win8.1 某应用渲染抛出 OutOfMemoryException 异常及修复方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%95%8C%E9%9D%A2%E6%89%93%E4%B8%8D%E5%BC%80%E6%8F%90%E7%A4%BA-System.ArithmeticException-Overflow-or-underflow-in-the-arithmetic-operation-%E5%BC%82%E5%B8%B8.html&quot;&gt;WPF 界面打不开提示 System.ArithmeticException Overflow or underflow in the arithmetic operation 异常&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%AD%A6%E6%83%95%E4%BD%BF%E7%94%A8-Dispatcher.InvokeShutdown-%E6%96%B9%E6%B3%95%E9%80%80%E5%87%BA%E5%BA%94%E7%94%A8-%E5%B0%86%E4%B8%8D%E8%A7%A6%E5%8F%91-Application.Exit-%E4%BA%8B%E4%BB%B6.html&quot;&gt;WPF 警惕使用 Dispatcher.InvokeShutdown 方法退出应用 将不触发 Application.Exit 事件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E4%BC%A0%E5%85%A5%E9%94%99%E8%AF%AF%E6%95%B0%E6%8D%AE%E7%BB%99%E5%88%B0-WriteableBitmap-%E5%8F%AF%E8%83%BD%E5%AF%BC%E8%87%B4%E6%B8%B2%E6%9F%93%E7%BA%BF%E7%A8%8B%E9%94%81%E4%BD%8F.html&quot;&gt;WPF 已知问题 传入错误数据给到 WriteableBitmap 可能导致渲染线程锁住&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-Separator-%E6%97%A0%E6%B3%95%E5%BA%94%E7%94%A8-ContextMenu-%E5%AE%9A%E4%B9%89%E7%9A%84%E9%BB%98%E8%AE%A4%E6%A0%B7%E5%BC%8F.html&quot;&gt;WPF 已知问题 Separator 无法应用 ContextMenu 定义的默认样式&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E6%B8%85%E7%A9%BA-CollectionView-%E7%9A%84-SortDescriptions-%E5%8F%AF%E8%83%BD%E6%8A%9B%E5%87%BA%E7%A9%BA%E5%BC%82%E5%B8%B8.html&quot;&gt;WPF 已知问题 清空 CollectionView 的 SortDescriptions 可能抛出空异常&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E5%9C%A8-ObservableCollection-%E7%9A%84-CollectionChanged-%E4%BF%AE%E6%94%B9%E9%9B%86%E5%90%88%E5%86%85%E5%AE%B9%E5%B0%86%E8%AE%A9-UI-%E6%98%BE%E7%A4%BA%E9%94%99%E8%AF%AF.html&quot;&gt;WPF 已知问题 在 ObservableCollection 的 CollectionChanged 修改集合内容将让 UI 显示错误&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E5%BC%80%E5%90%AF-IsManipulationEnabled-%E4%B9%8B%E5%90%8E%E8%A7%A6%E6%91%B8%E9%95%BF%E6%8C%89-RepeatButton-%E4%B8%8D%E4%BC%9A%E8%A7%A6%E5%8F%91%E8%BF%9E%E7%BB%AD%E7%9A%84-Click-%E4%BA%8B%E4%BB%B6.html&quot;&gt;WPF 已知问题 开启 IsManipulationEnabled 之后触摸长按 RepeatButton 不会触发连续的 Click 事件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E5%9C%A8-WIC-%E5%B1%82%E5%A4%84%E7%90%86%E5%BC%82%E5%B8%B8%E5%9B%BE%E7%89%87%E6%97%B6-%E5%8F%AF%E8%83%BD%E7%94%B1%E4%BA%8E%E5%87%BA%E7%8E%B0%E6%9C%AA%E5%A4%84%E7%90%86%E5%BC%82%E5%B8%B8%E5%AF%BC%E8%87%B4%E8%BF%9B%E7%A8%8B%E9%80%80%E5%87%BA.html&quot;&gt;WPF 已知问题 在 WIC 层处理异常图片时 可能由于出现未处理异常导致进程退出&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AE%B0-QEMU-%E8%99%9A%E6%8B%9F%E7%A3%81%E7%9B%98%E8%AE%BE%E5%A4%87%E7%A7%BB%E5%8A%A8%E6%96%87%E4%BB%B6%E6%8A%9B%E5%BC%82%E5%B8%B8%E4%BD%86%E5%AE%9E%E9%99%85%E7%A7%BB%E5%8A%A8%E6%88%90%E5%8A%9F.html&quot;&gt;记 QEMU 虚拟磁盘设备移动文件抛异常但实际移动成功&lt;/a&gt;
&lt;!-- [记 QEMU 虚拟磁盘设备移动文件抛异常但实际移动成功 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18547263 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E4%BD%BF%E7%94%A8-WindowChrome-%E5%9C%A8%E5%88%87%E6%8D%A2%E7%B3%BB%E7%BB%9F%E4%B8%BB%E9%A2%98%E8%89%B2%E6%97%B6%E5%AF%BC%E8%87%B4%E7%AA%97%E5%8F%A3%E7%95%8C%E9%9D%A2%E5%81%8F%E7%A7%BB.html&quot;&gt;WPF 已知问题 使用 WindowChrome 在切换系统主题色时导致窗口界面偏移&lt;/a&gt;
&lt;!-- [WPF 已知问题 使用 WindowChrome 在切换系统主题色时导致窗口界面偏移 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19185921 ) --&gt;&lt;/p&gt;

&lt;h4 id=&quot;其他软件导致的问题&quot;&gt;其他软件导致的问题&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%A2%AB%E8%BE%93%E5%85%A5%E6%B3%95%E5%B8%A6%E5%B4%A9%E8%BF%9B%E7%A8%8B.html&quot;&gt;WPF 被输入法带崩进程&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%85%A8%E5%B1%8F%E7%AA%97%E5%8F%A3%E5%B0%86%E8%AE%A9-Chrome-97-%E8%A7%86%E9%A2%91%E5%81%9C%E6%AD%A2%E6%92%AD%E6%94%BE.html&quot;&gt;WPF 全屏窗口将让 Chrome 97 视频停止播放&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AE%B0%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE%E5%AF%BC%E8%87%B4-WPF-%E5%BA%94%E7%94%A8%E5%8D%A1%E4%BD%8F-%E7%AA%97%E5%8F%A3%E6%97%A0%E6%B3%95%E6%BF%80%E6%B4%BB%E9%97%AE%E9%A2%98.html&quot;&gt;记微信截图导致 WPF 应用卡住 窗口无法激活问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AE%B0%E8%85%BE%E8%AE%AF%E5%BE%AE%E4%BF%A1%E8%BE%93%E5%85%A5%E6%B3%95%E5%AF%BC%E8%87%B4-WPF-%E5%BA%94%E7%94%A8%E5%8D%A1%E4%BD%8F-%E7%AA%97%E5%8F%A3%E6%97%A0%E6%B3%95%E6%BF%80%E6%B4%BB%E5%92%8C%E6%98%BE%E7%A4%BA.html&quot;&gt;记腾讯微信输入法导致 WPF 应用卡住 窗口无法激活和显示&lt;/a&gt;
&lt;!-- [记腾讯微信输入法导致 WPF 应用卡住 窗口无法激活和显示 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18559797 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%A2%AB-%E7%81%B5%E6%A0%BC%E6%96%AF%E7%BF%BB%E8%AF%91%E5%AE%98-%E5%8F%96%E8%AF%8D%E5%B8%A6%E5%B4%A9.html&quot;&gt;WPF 被 灵格斯翻译官 取词带崩&lt;/a&gt;
&lt;!-- [WPF 被 灵格斯翻译官 取词带崩 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18555988 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AE%B0-YKQQClean-%E5%AF%BC%E8%87%B4%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E7%95%8C%E9%9D%A2%E7%AA%97%E5%8F%A3%E5%BC%B9%E5%87%BA%E5%A4%B1%E8%B4%A5.html&quot;&gt;记 YKQQClean 导致应用程序界面窗口弹出失败&lt;/a&gt;
&lt;!-- [记 YKQQClean 导致应用程序界面窗口弹出失败 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19767237 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Office-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-GROOVEEX.DLL-%E5%B8%A6%E5%B4%A9%E8%BF%9B%E7%A8%8B.html&quot;&gt;Office 已知问题 GROOVEEX.DLL 带崩进程&lt;/a&gt;
&lt;!-- [Office 已知问题 GROOVEEX.DLL 带崩进程 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19244702 ) --&gt;&lt;/p&gt;

&lt;h4 id=&quot;已知驱动问题&quot;&gt;已知驱动问题&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AE%B0%E5%9B%A0%E4%B8%BA-NVIDIA-%E6%98%BE%E9%A9%B1%E9%94%99%E8%AF%AF%E8%80%8C%E8%AE%A9-WPF-%E5%BA%94%E7%94%A8%E5%90%AF%E5%8A%A8%E9%97%AA%E9%80%80%E9%97%AE%E9%A2%98.html&quot;&gt;记因为 NVIDIA 显驱错误而让 WPF 应用启动闪退问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%9A%84-WriteableBitmap-%E5%9C%A8-Intel-11-%E4%BB%A3-Iris-Xe-Graphics-%E6%A0%B8%E6%98%BE%E8%AE%BE%E5%A4%87%E4%B8%8A%E5%81%9C%E6%AD%A2%E6%B8%B2%E6%9F%93.html&quot;&gt;WPF 的 WriteableBitmap 在 Intel 11 代 Iris Xe Graphics 核显设备上停止渲染&lt;/a&gt; &lt;a href=&quot;https://cloud.tencent.com/developer/article/2379099&quot;&gt;腾讯云&lt;/a&gt;
&lt;!-- [WPF 的 WriteableBitmap 在 Intel 11 代 Iris Xe Graphics 核显设备上停止渲染 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/17962054 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%9A%84-Viewport3D-%E7%AD%89-3D-%E6%A8%A1%E5%9D%97%E5%9C%A8%E5%B8%A6-Intel-UHD-770-%E8%AE%BE%E5%A4%87%E4%B8%8A%E6%8A%9B%E5%87%BA%E6%B8%B2%E6%9F%93%E5%BC%82%E5%B8%B8.html&quot;&gt;WPF 的 Viewport3D 等 3D 模块在带 Intel UHD 770 设备上抛出渲染异常&lt;/a&gt; &lt;a href=&quot;https://cloud.tencent.com/developer/article/2379510&quot;&gt;腾讯云&lt;/a&gt;
&lt;!-- [WPF 的 Viewport3D 等 3D 模块在带 Intel UHD 770 设备上抛出渲染异常 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/17964618 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AE%B0-Intel-%E7%9A%84-31.0.101.5186-%E7%89%88%E6%9C%AC%E9%A9%B1%E5%8A%A8%E5%B8%A6%E5%B4%A9-WPF-%E7%A8%8B%E5%BA%8F.html&quot;&gt;记 Intel 的 31.0.101.5186 版本驱动带崩 WPF 程序&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;各版本已知问题和新功能&quot;&gt;各版本已知问题和新功能&lt;/h3&gt;

&lt;p&gt;以下的版本记录的是新功能加入的版本，以及发现问题的版本。有些问题会延续多个版本，不代表新版本就没有对应的问题，具体问题修复版本请参阅具体的博客&lt;/p&gt;

&lt;h4 id=&quot;dotnet-core-31&quot;&gt;dotnet core 3.1&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%8D%87%E7%BA%A7-.NET-Core-%E7%9A%84%E7%90%86%E7%94%B1.html&quot;&gt;WPF 升级 .NET Core 的理由&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%8F%8C%E5%90%91%E7%BB%91%E5%AE%9A%E5%88%B0%E9%9D%9E%E5%85%AC%E5%BC%80-set-%E6%96%B9%E6%B3%95%E5%B1%9E%E6%80%A7%E5%9C%A8-NET-45-%E5%92%8C-NET-Core-%E8%A1%8C%E4%B8%BA%E7%9A%84%E4%B8%8D%E5%90%8C.html&quot;&gt;WPF 双向绑定到非公开 set 方法属性在 NET 45 和 NET Core 行为的不同&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BB%8E-dotnet-core-3.0-%E7%9A%84%E7%89%B9%E6%80%A7%E8%AE%A9-WPF-%E5%B8%83%E5%B1%80%E5%A4%B1%E6%95%88%E8%AE%A8%E8%AE%BA-API-%E5%85%BC%E5%AE%B9.html&quot;&gt;从 dotnet core 3.0 的特性让 WPF 布局失效讨论 API 兼容&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%9C%A8-.NET-Core-3.1.19-%E7%89%88%E6%9C%AC-%E8%A7%A6%E6%91%B8%E7%AC%94%E8%BF%B9%E5%81%8F%E7%A7%BB%E9%97%AE%E9%A2%98.html&quot;&gt;WPF 在 .NET Core 3.1.19 版本 触摸笔迹偏移问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%9C%A8-.NET-Core-3.1.19-%E7%89%88%E6%9C%AC%E6%B2%A1%E6%9C%89%E8%B7%9F%E9%9A%8F-DPI-%E7%BC%A9%E6%94%BE%E6%96%87%E6%9C%AC%E8%BF%87%E5%B0%8F%E9%97%AE%E9%A2%98.html&quot;&gt;WPF 在 .NET Core 3.1.19 版本没有跟随 DPI 缩放文本过小问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-core-3.1-%E5%B0%86-UWP-%E6%8E%A7%E4%BB%B6%E5%B5%8C%E5%85%A5%E5%88%B0-WPF-%E5%BA%94%E7%94%A8-%E6%94%B6%E5%88%B0-UIA-%E6%B6%88%E6%81%AF%E4%B8%BB%E7%BA%BF%E7%A8%8B%E5%8D%A1%E4%BD%8F.html&quot;&gt;dotnet core 3.1 将 UWP 控件嵌入到 WPF 应用 收到 UIA 消息主线程卡住&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;dotnet-5-1&quot;&gt;dotnet 5&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BB%8E-dotnet-core-3-%E5%88%B0-dotnet-5-%E7%9A%84%E5%8F%98%E6%9B%B4.html&quot;&gt;WPF 从 dotnet core 3 到 dotnet 5 的变更&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-5-%E8%AE%A9-WPF-%E8%B0%83%E7%94%A8-WindowsRuntime-%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet 5 让 WPF 调用 WindowsRuntime 方法&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;dotnet-6-1&quot;&gt;dotnet 6&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-dotnet-6-%E8%AE%BE%E7%BD%AE-InvariantGlobalization-%E4%B9%8B%E5%90%8E%E5%B0%86%E4%B8%A2%E5%A4%B1%E9%BB%98%E8%AE%A4%E7%BB%91%E5%AE%9A%E8%BD%AC%E6%8D%A2%E5%AF%BC%E8%87%B4-XAML-%E6%8A%9B%E5%87%BA%E5%BC%82%E5%B8%B8.html&quot;&gt;WPF 已知问题 dotnet 6 设置 InvariantGlobalization 之后将丢失默认绑定转换导致 XAML 抛出异常&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-dotnet-6-%E5%BC%80%E5%90%AF-PM-v2-%E7%9A%84-DPI-%E6%84%9F%E7%9F%A5-%E5%AF%BC%E8%87%B4%E8%A7%A6%E6%91%B8%E7%BA%BF%E7%A8%8B%E8%AE%BF%E9%97%AE-UI-%E5%B1%9E%E6%80%A7%E6%8A%9B%E5%BC%82%E5%B8%B8.html&quot;&gt;WPF dotnet 6 开启 PM v2 的 DPI 感知 导致触摸线程访问 UI 属性抛异常&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BF%AE%E5%A4%8D%E5%BC%95%E7%94%A8%E5%BA%93%E6%8A%A5%E9%94%99-%E6%89%80%E4%BD%BF%E7%94%A8%E7%9A%84-PresentationFramework-6.0.2-%E9%AB%98%E4%BA%8E%E6%89%80%E5%BC%95%E7%94%A8%E7%9A%84%E6%A0%87%E8%AF%86%E4%B8%BA-6.0.0-%E7%A8%8B%E5%BA%8F%E9%9B%86.html&quot;&gt;WPF 修复引用库报错 所使用的 PresentationFramework 6.0.2 高于所引用的标识为 6.0.0 程序集&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;dotnet-7-1&quot;&gt;dotnet 7&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-7-WPF-%E7%A0%B4%E5%9D%8F%E6%80%A7%E6%94%B9%E5%8A%A8-%E6%8C%89%E4%B8%8B-F3-%E8%AE%A9-DataGrid-%E8%87%AA%E5%8A%A8%E6%8E%92%E5%BA%8F.html&quot;&gt;dotnet 7 WPF 破坏性改动 按下 F3 让 DataGrid 自动排序&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-7-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-WPF-%E7%9A%84-TreeView-%E5%BC%80%E5%90%AF%E8%99%9A%E6%8B%9F%E5%8C%96%E4%B9%8B%E5%90%8E%E5%8F%AA%E6%98%BE%E7%A4%BA%E9%A6%96%E9%A1%B9.html&quot;&gt;dotnet 7 已知问题 WPF 的 TreeView 开启虚拟化之后只显示首项&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;dotnet-8-1&quot;&gt;dotnet 8&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-8-WPF-%E6%94%AF%E6%8C%81%E5%9C%A8-RDP-%E8%BF%9C%E7%A8%8B%E6%A1%8C%E9%9D%A2%E7%8A%B6%E6%80%81%E4%B8%8B%E5%90%AF%E7%94%A8%E6%B8%B2%E6%9F%93%E7%A1%AC%E4%BB%B6%E5%8A%A0%E9%80%9F.html&quot;&gt;dotnet 8 WPF 支持在 RDP 远程桌面状态下启用渲染硬件加速&lt;/a&gt; &lt;a href=&quot;https://www.cnblogs.com/lindexi/p/17756214.html&quot;&gt;博客园&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AE%B0-dotnet-8.0.4-%E4%BF%AE%E5%A4%8D%E7%9A%84-WPF-%E7%9A%84%E8%A7%A6%E6%91%B8%E6%A8%A1%E5%9D%97%E5%AE%89%E5%85%A8%E9%97%AE%E9%A2%98.html&quot;&gt;记 dotnet 8.0.4 修复的 WPF 的触摸模块安全问题&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;dotnet-9-1&quot;&gt;dotnet 9&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-9-WPF-%E8%BF%9E%E5%AD%97%E7%AC%A6%E6%B8%B2%E6%9F%93%E6%94%AF%E6%8C%81.html&quot;&gt;dotnet 9 WPF 连字符渲染支持&lt;/a&gt;
&lt;!-- [dotnet 9 WPF 连字符渲染支持 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18545166 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-9-WPF-%E6%94%AF%E6%8C%81-Style-%E7%9A%84-Setter-%E5%A1%AB%E5%85%85%E5%86%85%E5%AE%B9%E6%97%B6%E5%8F%AF%E5%BF%BD%E7%95%A5-Value-%E6%A0%87%E7%AD%BE.html&quot;&gt;dotnet 9 WPF 支持 Style 的 Setter 填充内容时可忽略 Value 标签&lt;/a&gt;
&lt;!-- [dotnet 9 WPF 支持 Style 的 Setter 填充内容时可忽略 Value 标签 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18181294 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/mingupupu/p/18277446&quot;&gt;WPF在.NET9中的重大更新：Windows 11 主题 - mingupupup - 博客园&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-9-WPF-%E9%A1%B9%E7%9B%AE%E7%A6%81%E7%94%A8-IncludePackageReferencesDuringMarkupCompilation-%E5%AF%BC%E8%87%B4%E6%BA%90%E4%BB%A3%E7%A0%81%E5%8C%85-XAML-%E6%9E%84%E5%BB%BA%E5%A4%B1%E8%B4%A5.html&quot;&gt;dotnet 9 WPF 项目禁用 IncludePackageReferencesDuringMarkupCompilation 导致源代码包 XAML 构建失败&lt;/a&gt;
&lt;!-- [dotnet 9 WPF 项目禁用 IncludePackageReferencesDuringMarkupCompilation 导致源代码包 XAML 构建失败 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18555989 ) --&gt;&lt;/p&gt;

&lt;h4 id=&quot;dotnet-10&quot;&gt;dotnet 10&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-10-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E6%9E%84%E5%BB%BA-WPF-%E6%97%B6%E6%8F%90%E7%A4%BA-System.Private.Windows.GdiPlus-%E7%A8%8B%E5%BA%8F%E9%9B%86%E6%9C%AA%E6%89%BE%E5%88%B0%E9%94%99%E8%AF%AF.html&quot;&gt;dotnet 10 已知问题 构建 WPF 时提示 System.Private.Windows.GdiPlus 程序集未找到错误&lt;/a&gt;
&lt;!-- [dotnet 10 已知问题 构建 WPF 时提示 System.Private.Windows.GdiPlus 程序集未找到错误 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19224133 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-10-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-WinForms-%E7%9A%84-TargetFramework-%E4%B8%8E-System.Drawing.Common-%E4%B8%8D%E5%8C%B9%E9%85%8D%E5%B0%86%E6%8A%9B%E5%87%BA%E6%89%BE%E4%B8%8D%E5%88%B0%E7%B1%BB%E5%9E%8B%E5%BC%82%E5%B8%B8.html&quot;&gt;dotnet 10 已知问题 WinForms 的 TargetFramework 与 System.Drawing.Common 不匹配将抛出找不到类型异常&lt;/a&gt;
&lt;!-- [dotnet 10 已知问题 WinForms 的 TargetFramework 与 System.Drawing.Common 不匹配将抛出找不到类型异常 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19360020 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;构建发布&quot;&gt;构建发布&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E9%80%9A%E8%BF%87-Windows-Template-Studio-%E5%BF%AB%E9%80%9F%E6%90%AD%E5%BB%BA%E9%A1%B9%E7%9B%AE%E6%A1%86%E6%9E%B6%E5%92%8C%E4%B8%8A%E6%89%8B%E9%A1%B9%E7%9B%AE.html&quot;&gt;WPF 通过 Windows Template Studio 快速搭建项目框架和上手项目&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E9%80%9A%E8%BF%87-ReadyToRun-%E6%8F%90%E5%8D%87%E6%80%A7%E8%83%BD.html&quot;&gt;WPF 通过 ReadyToRun 提升性能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%9F%BA%E4%BA%8E-.NET-5-%E6%A1%86%E6%9E%B6%E5%92%8C-.NET-6-%E7%9A%84-SDK-%E8%BF%9B%E8%A1%8C%E5%AE%8C%E5%85%A8%E5%8D%95%E6%96%87%E4%BB%B6%E5%8F%91%E5%B8%83.html&quot;&gt;WPF 基于 .NET 5 框架和 .NET 6 的 SDK 进行完全单文件发布&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BF%AE%E5%A4%8D-dotnet-6-%E4%B8%8E%E6%BA%90%E4%BB%A3%E7%A0%81%E5%8C%85%E5%86%B2%E7%AA%81.html&quot;&gt;WPF 修复 dotnet 6 与源代码包冲突&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%AE%BE%E7%BD%AE-IncludePackageReferencesDuringMarkupCompilation-%E5%B1%9E%E6%80%A7%E5%AF%BC%E8%87%B4%E5%88%86%E6%9E%90%E5%99%A8%E4%B8%8D%E5%B7%A5%E4%BD%9C.html&quot;&gt;WPF 设置 IncludePackageReferencesDuringMarkupCompilation 属性导致分析器不工作&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E9%80%9A%E8%BF%87-dotnet-core-%E5%8F%91%E5%B8%83%E5%8D%95%E6%96%87%E4%BB%B6%E6%97%B6-log4net-%E6%97%A0%E6%B3%95%E4%BD%BF%E7%94%A8.html&quot;&gt;WPF 通过 dotnet core 发布单文件时 log4net 无法使用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%A8%8B%E5%BA%8F%E7%94%9F%E6%88%90%E7%B1%BB%E5%BA%93%E9%94%99%E8%AF%AF.html&quot;&gt;WPF 程序生成类库错误&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E9%A1%B9%E7%9B%AE%E6%96%87%E4%BB%B6%E4%B8%8D%E5%8A%A0-windows-%E7%9A%84%E5%BC%95%E7%94%A8-WPF-%E6%A1%86%E6%9E%B6%E6%96%B9%E5%BC%8F.html&quot;&gt;WPF 项目文件不加 -windows 的引用 WPF 框架方式&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BD%BF%E7%94%A8-SatelliteResourceLanguages-%E5%87%8F%E5%B0%91-WPF-%E5%8F%91%E5%B8%83%E7%9A%84%E5%A4%9A%E8%AF%AD%E8%A8%80%E6%96%87%E4%BB%B6%E5%A4%B9%E6%95%B0%E9%87%8F.html&quot;&gt;使用 SatelliteResourceLanguages 减少 WPF 发布的多语言文件夹数量&lt;/a&gt;
&lt;!-- [使用 SatelliteResourceLanguages 减少 WPF 发布的多语言文件夹数量 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18932216 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/SignTool-%E4%BD%BF%E7%94%A8-SafeNet-eToken-%E7%A1%AC%E8%AF%81%E4%B9%A6%E8%BF%9B%E8%A1%8C%E4%BB%A3%E7%A0%81%E7%AD%BE%E5%90%8D.html&quot;&gt;SignTool 使用 SafeNet eToken 硬证书进行代码签名&lt;/a&gt;
&lt;!-- [SignTool 使用 SafeNet eToken 硬证书进行代码签名 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19134167 ) --&gt;&lt;/p&gt;

&lt;h4 id=&quot;打包-uwp-应用&quot;&gt;打包 UWP 应用&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%88%A4%E6%96%AD%E5%BD%93%E5%89%8D%E5%BA%94%E7%94%A8%E8%A2%AB%E6%89%93%E5%8C%85%E4%B8%BA-UWP-%E8%80%8C%E8%BF%90%E8%A1%8C.html&quot;&gt;WPF 判断当前应用被打包为 UWP 而运行&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%BC%95%E7%94%A8-UWP-%E6%8E%A7%E4%BB%B6-%E4%B8%8D%E6%89%93%E5%8C%85%E4%B8%BA-MSIX-%E5%88%86%E5%8F%91%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 引用 UWP 控件 不打包为 MSIX 分发的方法&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;wpf-调试&quot;&gt;WPF 调试&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BE%9D%E8%B5%96%E5%B1%9E%E6%80%A7%E7%BB%91%E5%AE%9A%E4%B8%8D%E4%B8%8A%E8%B0%83%E8%AF%95%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 依赖属性绑定不上调试方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%A6%82%E4%BD%95%E8%B0%83%E8%AF%95-binding.html&quot;&gt;WPF 如何调试 binding&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%B0%83%E8%AF%95%E5%9B%A0%E4%B8%BA%E5%AF%B9%E8%B1%A1%E4%B8%8D%E5%90%8C%E8%80%8C%E7%BB%91%E5%AE%9A%E5%A4%B1%E6%95%88%E9%97%AE%E9%A2%98.html&quot;&gt;WPF 调试因为对象不同而绑定失效问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%9F%BA%E4%BA%8E-WER-%E6%B3%A8%E5%86%8C%E5%BA%94%E7%94%A8%E5%B4%A9%E6%BA%83%E6%97%A0%E5%93%8D%E5%BA%94%E5%9B%9E%E8%B0%83%E5%92%8C%E9%87%8D%E5%90%AF%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 基于 WER 注册应用崩溃无响应回调和重启方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%B0%83%E8%AF%95-%E8%8E%B7%E5%BE%97%E8%BF%BD%E8%B8%AA%E8%BE%93%E5%87%BA.html&quot;&gt;WPF 调试 获得追踪输出&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%88%A4%E6%96%AD%E8%B0%83%E7%94%A8%E6%96%B9%E6%B3%95%E5%A0%86%E6%A0%88.html&quot;&gt;WPF 判断调用方法堆栈&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%9F%90%E4%B8%AA%E7%95%8C%E9%9D%A2%E6%88%96%E6%8E%A7%E4%BB%B6%E5%9C%A8%E7%95%8C%E9%9D%A2%E6%89%BE%E4%B8%8D%E5%88%B0%E7%9C%8B%E4%B8%8D%E5%88%B0%E5%8F%AF%E8%83%BD%E7%9A%84%E5%8E%9F%E5%9B%A0.html&quot;&gt;WPF 某个界面或控件在界面找不到看不到可能的原因&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%8E%B7%E5%8F%96%E6%98%AF%E5%93%AA%E4%B8%AA%E8%BF%9B%E7%A8%8B%E5%8D%A0%E7%94%A8%E4%BA%86%E6%96%87%E4%BB%B6.html&quot;&gt;WPF 获取是哪个进程占用了文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%A6%82%E4%BD%95%E7%9F%A5%E9%81%93%E5%BD%93%E5%89%8D%E6%9C%89%E5%A4%9A%E5%B0%91%E4%B8%AA-DispatcherTimer-%E5%9C%A8%E8%BF%90%E8%A1%8C.html&quot;&gt;WPF 如何知道当前有多少个 DispatcherTimer 在运行&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Windows-%E4%B8%8B%E7%9A%84-WPF-%E5%BC%80%E5%8F%91-%E8%B0%83%E8%AF%95%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E5%9C%A8%E4%BB%80%E4%B9%88%E6%97%B6%E6%9C%BA%E5%8A%A0%E8%BD%BD%E4%BA%86-Dll-%E6%A8%A1%E5%9D%97.html&quot;&gt;Windows 下的 WPF 开发 调试应用程序在什么时机加载了 Dll 模块&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%A6%82%E4%BD%95%E5%9C%A8%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E8%B0%83%E8%AF%95%E5%90%AF%E5%8A%A8.html&quot;&gt;WPF 如何在应用程序调试启动&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%A6%82%E4%BD%95%E4%BF%9D%E7%95%99-wpftmp.csproj-%E6%96%87%E4%BB%B6%E7%94%A8%E4%BA%8E%E8%B0%83%E8%AF%95.html&quot;&gt;如何保留 wpftmp.csproj 文件用于调试&lt;/a&gt;
&lt;!-- [如何保留 wpftmp.csproj 文件用于调试 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18564012 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;读-wpf-源代码&quot;&gt;读 WPF 源代码&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%BA%90%E4%BB%A3%E7%A0%81-%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E5%86%99%E4%B8%80%E4%B8%AA-UI-%E6%A1%86%E6%9E%B6.html&quot;&gt;WPF 源代码 从零开始写一个 UI 框架&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-%E5%B8%83%E5%B1%80%E6%97%B6-Arrange-%E5%A6%82%E4%BD%95%E5%BD%B1%E5%93%8D%E5%85%83%E7%B4%A0%E6%B8%B2%E6%9F%93%E5%9D%90%E6%A0%87.html&quot;&gt;dotnet 读 WPF 源代码笔记 布局时 Arrange 如何影响元素渲染坐标&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-%E4%B8%BA%E4%BB%80%E4%B9%88%E8%87%AA%E5%AE%9A%E4%B9%89%E7%9A%84-UserControl-%E7%94%A8%E6%88%B7%E6%8E%A7%E4%BB%B6%E4%B8%8D%E8%83%BD%E8%B7%A8%E7%A8%8B%E5%BA%8F%E9%9B%86%E7%BB%A7%E6%89%BF.html&quot;&gt;dotnet 读 WPF 源代码笔记 为什么自定义的 UserControl 用户控件不能跨程序集继承&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-WPF-%E6%98%AF%E5%A6%82%E4%BD%95%E5%81%9A%E5%88%B0%E4%B8%80%E5%A5%97%E4%BB%A3%E7%A0%81%E5%85%BC%E5%AE%B9%E5%A4%9A%E4%B8%AA-.NET-Framework-%E7%89%88%E6%9C%AC.html&quot;&gt;dotnet 读 WPF 源代码笔记 WPF 是如何做到一套代码兼容多个 .NET Framework 版本&lt;/a&gt;
&lt;!-- [dotnet 读 WPF 源代码笔记 WPF 是如何做到一套代码兼容多个 .NET Framework 版本 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/16697734.html ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-%E4%B8%BA%E4%BB%80%E4%B9%88%E5%8A%A0%E4%B8%8A-BooleanBoxes-%E7%B1%BB.html&quot;&gt;dotnet 读 WPF 源代码笔记 为什么加上 BooleanBoxes 类&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-%E6%B8%B2%E6%9F%93%E6%94%B6%E9%9B%86%E6%98%AF%E5%A6%82%E4%BD%95%E8%A7%A6%E5%8F%91.html&quot;&gt;dotnet 读 WPF 源代码笔记 渲染收集是如何触发&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-%E4%BA%86%E8%A7%A3-WPF-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E5%90%8E%E5%8F%B0%E7%BA%BF%E7%A8%8B%E5%88%9B%E5%BB%BA-WriteableBitmap-%E9%94%81%E4%BD%8F%E4%B8%BB%E7%BA%BF%E7%A8%8B.html&quot;&gt;dotnet 读 WPF 源代码笔记 了解 WPF 已知问题 后台线程创建 WriteableBitmap 锁住主线程&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-WriteableBitmap-%E7%9A%84%E6%B8%B2%E6%9F%93%E5%92%8C%E6%9B%B4%E6%96%B0%E6%98%AF%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0.html&quot;&gt;dotnet 读 WPF 源代码笔记 WriteableBitmap 的渲染和更新是如何实现&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BB%8E-WriteableBitmap-%E9%87%8C%E8%8E%B7%E5%8F%96%E5%88%B0%E6%B8%B2%E6%9F%93%E7%BA%BF%E7%A8%8B%E4%BD%BF%E7%94%A8%E7%9A%84-IWICBitmap-%E5%AF%B9%E8%B1%A1.html&quot;&gt;WPF 从 WriteableBitmap 里获取到渲染线程使用的 IWICBitmap 对象&lt;/a&gt;
&lt;!-- [WPF 从 WriteableBitmap 里获取到渲染线程使用的 IWICBitmap 对象 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18843888 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-%E6%8F%90%E5%8D%87%E8%B0%83%E8%AF%95%E6%95%88%E7%8E%87%E7%9A%84-NamedObject-%E7%B1%BB%E5%9E%8B.html&quot;&gt;dotnet 读 WPF 源代码笔记 提升调试效率的 NamedObject 类型&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-%E6%8F%92%E5%85%A5%E8%A7%A6%E6%91%B8%E8%AE%BE%E5%A4%87%E7%9A%84%E5%88%9D%E5%A7%8B%E5%8C%96%E8%8E%B7%E5%8F%96%E8%AE%BE%E5%A4%87%E4%BF%A1%E6%81%AF.html&quot;&gt;dotnet 读 WPF 源代码笔记 插入触摸设备的初始化获取设备信息&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-%E4%BA%86%E8%A7%A3-WPF-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E7%94%A8%E6%88%B7%E8%AE%BE%E5%A4%87%E4%B8%8A%E4%B8%8D%E5%AD%98%E5%9C%A8-Arial-%E5%AD%97%E4%BD%93%E5%B0%86%E5%AF%BC%E8%87%B4%E5%BA%94%E7%94%A8%E9%97%AA%E9%80%80.html&quot;&gt;dotnet 读 WPF 源代码笔记 了解 WPF 已知问题 用户设备上不存在 Arial 字体将导致应用闪退&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-%E5%88%9B%E5%BB%BA-SolidColorBrush-%E6%80%A7%E8%83%BD%E6%B2%A1%E6%9C%89%E6%83%B3%E8%B1%A1%E9%82%A3%E4%B9%88%E5%B7%AE.html&quot;&gt;dotnet 读 WPF 源代码笔记 创建 SolidColorBrush 性能没有想象那么差&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-%E9%BB%98%E8%AE%A4%E7%9A%84-Main-%E5%87%BD%E6%95%B0%E6%98%AF%E5%9C%A8%E5%93%AA%E5%88%9B%E5%BB%BA%E7%9A%84.html&quot;&gt;dotnet 读 WPF 源代码笔记 默认的 Main 函数是在哪创建的&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-%E4%B8%BA%E4%BB%80%E4%B9%88%E8%AE%BE%E7%BD%AE%E4%BA%86SplashScreen%E4%BC%9A%E8%AE%A9Application.Current.Activated%E4%BA%8B%E4%BB%B6%E4%B8%8D%E8%A7%A6%E5%8F%91.html&quot;&gt;dotnet 读 WPF 源代码笔记 为什么设置了SplashScreen会让Application.Current.Activated事件不触发&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-SafeMILHandleMemoryPressure-%E7%9A%84%E4%BD%9C%E7%94%A8.html&quot;&gt;dotnet 读 WPF 源代码笔记 SafeMILHandleMemoryPressure 的作用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-%E4%B8%BA%E4%BB%80%E4%B9%88%E9%BB%98%E8%AE%A4%E7%9A%84%E7%AC%94%E8%BF%B9%E8%A7%A6%E6%91%B8%E7%82%B9%E7%9A%84%E5%8E%8B%E6%84%9F%E6%98%AF-0.5-%E7%9A%84%E5%80%BC.html&quot;&gt;dotnet 读 WPF 源代码笔记 为什么默认的笔迹触摸点的压感是 0.5 的值&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-Stroke-%E7%B1%BB%E5%8F%AF%E8%83%BD%E5%AD%98%E5%9C%A8%E7%9A%84%E5%86%85%E5%AD%98%E6%B3%84%E9%9C%B2.html&quot;&gt;dotnet 读 WPF 源代码笔记 Stroke 类可能存在的内存泄露&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-XAML-%E5%88%9B%E5%BB%BA%E5%AF%B9%E8%B1%A1%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet 读 WPF 源代码笔记 XAML 创建对象的方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-%E4%BD%BF%E7%94%A8-Win32-%E6%96%B9%E6%B3%95%E4%BF%AE%E6%94%B9%E7%AA%97%E5%8F%A3%E7%9A%84%E5%9D%90%E6%A0%87%E5%92%8C%E5%A4%A7%E5%B0%8F%E5%AF%B9%E7%AA%97%E5%8F%A3%E4%BE%9D%E8%B5%96%E5%B1%9E%E6%80%A7%E7%9A%84%E5%BD%B1%E5%93%8D.html&quot;&gt;dotnet 读 WPF 源代码笔记 使用 Win32 方法修改窗口的坐标和大小对窗口依赖属性的影响&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-AppDomainShutdownMonitor-%E7%9A%84%E8%AE%BE%E8%AE%A1.html&quot;&gt;dotnet 读 WPF 源代码笔记 AppDomainShutdownMonitor 的设计&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-%E5%90%AF%E5%8A%A8%E6%AC%A2%E8%BF%8E%E7%95%8C%E9%9D%A2-SplashScreen-%E7%9A%84%E5%8E%9F%E7%90%86.html&quot;&gt;dotnet 读 WPF 源代码笔记 启动欢迎界面 SplashScreen 的原理&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-wpfgfx_cor3.dll-%E6%98%AF%E4%BB%80%E4%B9%88%E6%96%87%E4%BB%B6.html&quot;&gt;dotnet 读 WPF 源代码笔记 wpfgfx_cor3.dll 是什么文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-WIC-%E5%A4%9A%E5%AA%92%E4%BD%93%E5%9B%BE%E7%89%87%E5%A4%84%E7%90%86%E9%80%9A%E8%BF%87-WindowsCodecs.dll-%E5%AE%9E%E7%8E%B0%E5%8A%9F%E8%83%BD.html&quot;&gt;dotnet 读 WPF 源代码笔记 WIC 多媒体图片处理通过 WindowsCodecs.dll 实现功能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-Framework-%E6%BA%90%E4%BB%A3%E7%A0%81-Ink.html&quot;&gt;dotnet Framework 源代码 · Ink&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-Framework-%E6%BA%90%E4%BB%A3%E7%A0%81-ScrollViewer.html&quot;&gt;dotnet Framework 源代码 · ScrollViewer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81-Popup-%E7%9A%84-StaysOpen-%E4%B8%BA-false-%E5%B0%86%E4%BC%9A%E5%90%83%E6%8E%89%E5%85%B6%E4%BB%96%E7%AA%97%E5%8F%A3%E7%9A%84%E9%A6%96%E6%AC%A1%E6%BF%80%E6%B4%BB.html&quot;&gt;dotnet 读 WPF 源代码 Popup 的 StaysOpen 为 false 将会吃掉其他窗口的首次激活&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81-%E8%81%8A%E8%81%8A-DispatcherTimer-%E7%9A%84%E5%AE%9E%E7%8E%B0.html&quot;&gt;dotnet 读 WPF 源代码 聊聊 DispatcherTimer 的实现&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81-%E5%AD%A6%E4%B9%A0%E4%BD%BF%E7%94%A8-Microsoft.DotNet.Arcade.Sdk-%E5%A4%84%E7%90%86%E4%BB%A3%E7%A0%81%E9%87%8C%E7%9A%84%E5%A4%9A%E8%AF%AD%E8%A8%80.html&quot;&gt;dotnet 读 WPF 源代码 学习使用 Microsoft.DotNet.Arcade.Sdk 处理代码里的多语言&lt;/a&gt;
&lt;!-- [dotnet 读 WPF 源代码 学习使用 Microsoft.DotNet.Arcade.Sdk 处理代码里的多语言 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19198245 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%BA%90%E4%BB%A3%E7%A0%81-%E8%B5%84%E6%BA%90%E5%AD%97%E5%85%B8-ResourceDictionary-%E8%AE%BE%E7%BD%AE-Source-%E5%B1%9E%E6%80%A7%E7%9A%84%E9%80%BB%E8%BE%91.html&quot;&gt;WPF 源代码 资源字典 ResourceDictionary 设置 Source 属性的逻辑&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-%E8%81%8A%E8%81%8A-HwndWrapper.GetGCMemMessage-%E8%B0%83%E8%AF%95%E6%B6%88%E6%81%AF.html&quot;&gt;dotnet 读 WPF 源代码笔记 聊聊 HwndWrapper.GetGCMemMessage 调试消息&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%86%B7%E7%9F%A5%E8%AF%86-%E5%AE%9A%E4%B9%89%E4%BE%9D%E8%B5%96%E5%B1%9E%E6%80%A7%E7%9A%84%E6%9C%80%E5%A4%A7%E6%95%B0%E9%87%8F%E6%98%AF-65534-%E4%B8%AA.html&quot;&gt;WPF 冷知识 定义依赖属性的最大数量是 65534 个&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-UncommonField-%E7%B1%BB%E5%9E%8B%E6%98%AF%E4%BB%80%E4%B9%88.html&quot;&gt;WPF UncommonField 类型是什么&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%A1%86%E6%9E%B6%E5%BC%80%E5%8F%91-ColumnDefinition-%E5%92%8C-RowDefinition-%E7%9A%84%E4%BB%A3%E7%A0%81%E5%9C%A8%E5%93%AA.html&quot;&gt;WPF 框架开发 ColumnDefinition 和 RowDefinition 的代码在哪&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%AE%B2%E8%AE%B2-Microsoft.NET.Sdk.WindowsDesktop-%E7%9A%84%E5%8E%9F%E7%90%86.html&quot;&gt;WPF 讲讲 Microsoft.NET.Sdk.WindowsDesktop 的原理&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-ShowInTaskbar-%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86.html&quot;&gt;dotnet 读 WPF 源代码笔记 ShowInTaskbar 的实现原理&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;文本-1&quot;&gt;文本&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-%E7%AE%80%E5%8D%95%E8%81%8A%E8%81%8A%E6%96%87%E6%9C%AC%E5%B8%83%E5%B1%80%E6%8D%A2%E8%A1%8C%E9%80%BB%E8%BE%91.html&quot;&gt;dotnet 读 WPF 源代码笔记 简单聊聊文本布局换行逻辑&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-GlyphRun-%E7%9A%84-DeviceFontName-%E7%9A%84%E5%8A%9F%E8%83%BD%E6%98%AF%E4%BB%80%E4%B9%88.html&quot;&gt;dotnet 读 WPF 源代码笔记 GlyphRun 的 DeviceFontName 的功能是什么&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-%E6%B8%B2%E6%9F%93%E5%B1%82%E6%98%AF%E5%A6%82%E4%BD%95%E5%B0%86%E5%AD%97%E7%AC%A6-GlyphRun-%E7%94%BB%E5%87%BA%E6%9D%A5%E7%9A%84.html&quot;&gt;dotnet 读 WPF 源代码笔记 渲染层是如何将字符 GlyphRun 画出来的&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81-%E4%BA%86%E8%A7%A3%E8%8E%B7%E5%8F%96-GlyphTypeface-%E7%9A%84-CharacterToGlyphMap-%E7%9A%84%E6%95%B0%E9%87%8F%E8%80%97%E6%97%B6%E5%8E%9F%E5%9B%A0.html&quot;&gt;读 WPF 源代码 了解获取 GlyphTypeface 的 CharacterToGlyphMap 的数量耗时原因&lt;/a&gt;
&lt;!-- [读 WPF 源代码 了解获取 GlyphTypeface 的 CharacterToGlyphMap 的数量耗时原因 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19114691 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-%E8%81%8A%E8%81%8A-OpenType-%E5%AE%9A%E4%B9%89%E7%9A%84%E5%AD%97%E4%BD%93%E7%89%B9%E6%80%A7.html&quot;&gt;dotnet 读 WPF 源代码笔记 聊聊 OpenType 定义的字体特性&lt;/a&gt;
&lt;!-- [dotnet 读 WPF 源代码笔记 聊聊 OpenType 定义的字体特性 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19761702 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;wpf-框架开发&quot;&gt;WPF 框架开发&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%A6%82%E4%BD%95%E6%9E%84%E5%BB%BA-WPF-%E5%AE%98%E6%96%B9%E5%BC%80%E6%BA%90%E6%A1%86%E6%9E%B6%E6%BA%90%E4%BB%A3%E7%A0%81.html&quot;&gt;手把手教你如何构建 WPF 官方开源框架源代码&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E6%9E%84%E5%BB%BA-WPF-%E6%A1%86%E6%9E%B6%E7%9A%84%E7%A7%81%E6%9C%89%E7%89%88%E6%9C%AC.html&quot;&gt;手把手教你构建 WPF 框架的私有版本&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%A1%86%E6%9E%B6%E5%85%A8%E6%9E%84%E5%BB%BA%E7%8E%AF%E5%A2%83%E8%99%9A%E6%8B%9F%E6%9C%BA%E7%A1%AC%E7%9B%98%E5%88%86%E4%BA%AB.html&quot;&gt;WPF 框架全构建环境虚拟机硬盘分享&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%9C%80%E7%AE%80%E6%96%B9%E6%B3%95%E4%BD%BF%E7%94%A8%E8%87%AA%E5%B7%B1%E5%AE%9A%E5%88%B6%E7%9A%84-WPF-%E6%A1%86%E6%9E%B6.html&quot;&gt;WPF 最简方法使用自己定制的 WPF 框架&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%A1%86%E6%9E%B6%E5%BC%80%E5%8F%91-WPF-%E7%9A%84%E6%9E%84%E5%BB%BA%E5%9C%A8%E5%93%AA%E4%BD%BF%E7%94%A8%E5%88%B0-Perl-%E5%B7%A5%E5%85%B7.html&quot;&gt;WPF 框架开发 WPF 的构建在哪使用到 Perl 工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%A1%86%E6%9E%B6%E5%BC%80%E5%8F%91-%E6%9B%B4%E6%94%B9-API-%E4%B9%8B%E5%90%8E%E8%AE%A9-CI-%E9%87%8D%E6%96%B0%E5%88%9B%E5%BB%BA-API-%E5%85%BC%E5%AE%B9%E6%A3%80%E6%9F%A5%E5%9F%BA%E5%87%86.html&quot;&gt;WPF 框架开发 更改 API 之后让 CI 重新创建 API 兼容检查基准&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%A1%86%E6%9E%B6%E5%BC%80%E5%8F%91-%E5%8A%A0%E5%85%A5-InternalsVisibleToAttribute-%E7%89%B9%E6%80%A7%E8%AE%A9%E5%85%B6%E4%BB%96%E7%A8%8B%E5%BA%8F%E9%9B%86%E5%8F%AF%E4%BB%A5%E8%AE%BF%E9%97%AE-internal-%E6%9D%83%E9%99%90%E6%88%90%E5%91%98.html&quot;&gt;WPF 框架开发 加入 InternalsVisibleToAttribute 特性让其他程序集可以访问 internal 权限成员&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%A1%86%E6%9E%B6%E5%BC%80%E5%8F%91-%E8%B0%83%E8%AF%95%E5%92%8C%E5%BC%80%E5%8F%91-System.Xaml-%E7%9A%84%E7%8B%AC%E7%AB%8B%E9%A1%B9%E7%9B%AE%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 框架开发 调试和开发 System.Xaml 的独立项目方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%A1%86%E6%9E%B6%E5%BC%80%E5%8F%91-%E8%B0%83%E8%AF%95%E5%92%8C%E5%BC%80%E5%8F%91-XAML-%E6%9E%84%E5%BB%BA%E8%BF%87%E7%A8%8B%E7%9A%84-PresentationBuildTasks-%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 框架开发 调试和开发 XAML 构建过程的 PresentationBuildTasks 方法&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;uwp&quot;&gt;UWP&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E5%AD%A6%E4%B9%A0%E5%85%A5%E9%97%A8-Windows-10-%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F-UWP-%E5%BC%80%E5%8F%91.html&quot;&gt;从零开始学习入门 Windows 10 应用程序 UWP 开发&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%85%A5%E9%97%A8.html&quot;&gt;win10 uwp 入门&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%A6%82%E4%BD%95%E5%BC%80%E5%A7%8B%E5%86%99-uwp-%E7%A8%8B%E5%BA%8F.html&quot;&gt;win10 uwp 如何开始写 uwp 程序&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/UWP-WinUI-%E5%88%B6%E4%BD%9C%E4%B8%80%E4%B8%AA%E8%B7%AF%E5%BE%84%E7%9F%A2%E9%87%8F%E5%9B%BE%E6%A0%87%E6%8C%89%E9%92%AE%E6%A0%B7%E5%BC%8F%E5%85%A5%E9%97%A8.html&quot;&gt;UWP WinUI 制作一个路径矢量图标按钮样式入门&lt;/a&gt;
&lt;!-- [UWP WinUI 制作一个路径矢量图标按钮样式入门 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18288632 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/UWP-%E5%BC%80%E5%8F%91%E4%B8%AD-%E9%9C%80%E8%A6%81%E7%9F%A5%E9%81%93%E7%9A%841000%E4%B8%AA%E9%97%AE%E9%A2%98.html&quot;&gt;UWP 开发中，需要知道的1000个问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/UWP-%E5%92%8C-WPF-%E5%AF%B9%E6%AF%94.html&quot;&gt;UWP 和 WPF 对比&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E9%80%89%E6%8B%A9%E6%96%87%E6%9C%AC%E8%BD%AC%E8%AF%AD%E9%9F%B3%E7%9A%84%E6%9C%BA%E5%99%A8%E4%BA%BA.html&quot;&gt;win10 uwp 选择文本转语音的机器人&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E9%A2%9C%E8%89%B2%E8%BD%AC%E6%8D%A2.html&quot;&gt;win10 uwp 颜色转换&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;&lt;a href=&quot;/post/win10-uwp-%E5%A6%82%E4%BD%95%E5%88%A4%E6%96%AD%E4%B8%80%E4%B8%AA%E6%8E%A7%E4%BB%B6%E5%9C%A8%E6%BB%9A%E5%8A%A8%E6%9D%A1%E7%9A%84%E9%87%8C%E9%9D%A2%E6%98%AF%E7%94%A8%E6%88%B7%E5%8F%AF%E8%A7%81.html&quot;&gt;win10 uwp 如何判断一个控件在滚动条的里面是用户可见&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%88%97%E8%A1%A8%E6%A8%A1%E6%9D%BF%E9%80%89%E6%8B%A9%E5%99%A8.html&quot;&gt;win10 uwp 列表模板选择器&lt;/a&gt;
&lt;a href=&quot;/post/win10-UWP-Controls-by-function.html&quot;&gt;win10 UWP Controls by function&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E6%8F%90%E7%A4%BA-Cannot-find-a-Resource-with-the-Name-Key-%E6%89%BE%E4%B8%8D%E5%88%B0%E8%B5%84%E6%BA%90.html&quot;&gt;win10 uwp 提示 Cannot find a Resource with the Name Key 找不到资源&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E9%80%9A%E8%BF%87%E5%91%BD%E4%BB%A4%E8%A1%8C%E8%84%9A%E6%9C%AC%E5%BC%80%E5%90%AF%E6%97%81%E5%8A%A0%E8%BD%BD.html&quot;&gt;win10 uwp 通过命令行脚本开启旁加载&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%AE%BF%E9%97%AE%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88%E6%96%87%E4%BB%B6.html&quot;&gt;win10 uwp 访问解决方案文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%BD%95%E5%88%B6%E4%BB%BB%E6%84%8F%E5%BA%94%E7%94%A8%E5%B1%8F%E5%B9%95.html&quot;&gt;win10 uwp 录制任意应用屏幕&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E9%9D%9Eui%E7%BA%BF%E7%A8%8B%E8%AE%BF%E9%97%AE-ui.html&quot;&gt;win10 uwp 非ui线程访问 ui &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E7%9D%A1%E7%9C%A0%E5%94%A4%E9%86%92.html&quot;&gt;win10 uwp 睡眠唤醒&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E6%94%B6%E9%9B%86-DUMP-%E6%96%87%E4%BB%B6.html&quot;&gt;win10 uwp 收集 DUMP 文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E6%9C%AC%E6%96%87%E8%AF%B4%E5%A6%82%E4%BD%95%E6%98%BE%E7%A4%BASVG.html&quot;&gt;本文说如何显示SVG&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/UWP-how-to-get-the-touch-width.html&quot;&gt;UWP how to get the touch width&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-listView-%E7%BB%91%E5%AE%9A%E5%89%8D%E4%B8%80%E9%A1%B9.html&quot;&gt;win10 uwp listView 绑定前一项&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%AD%97%E7%AC%A6%E6%96%87%E6%9C%AC%E8%BD%AC%E8%AF%AD%E9%9F%B3%E5%A3%B0%E9%9F%B3%E6%96%87%E4%BB%B6%E6%96%B9%E6%B3%95.html&quot;&gt;win10 uwp 字符文本转语音声音文件方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E6%8D%95%E8%8E%B7%E5%90%8E%E5%8F%B0%E7%BA%BF%E7%A8%8B%E5%BC%82%E5%B8%B8.html&quot;&gt;win10 uwp 捕获后台线程异常&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%A6%82%E4%BD%95%E5%9C%A8-UWP-%E4%BD%BF%E7%94%A8-wpf-%E7%9A%84-Trigger.html&quot;&gt;如何在 UWP 使用 wpf 的 Trigger &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%BB%8E-Unity-%E5%88%9B%E5%BB%BA.html&quot;&gt;win10 uwp 从 Unity 创建&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win32-%E5%B5%8C%E5%85%A5%E7%AC%AC%E4%B8%89%E6%96%B9-UWP-%E5%BA%94%E7%94%A8%E5%81%9A%E5%BC%80%E6%94%BE%E5%B9%B3%E5%8F%B0.html&quot;&gt;win32 嵌入第三方 UWP 应用做开放平台&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%A6%82%E4%BD%95%E7%BB%99-DropDownButton-%E4%B8%80%E4%B8%AA%E5%BE%88%E5%B0%8F%E7%9A%84%E5%AE%BD%E5%BA%A6.html&quot;&gt;win10 uwp 如何给 DropDownButton 一个很小的宽度&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%BD%BF%E7%94%A8-OCR-%E5%85%89%E5%AD%A6%E5%AD%97%E7%AC%A6%E8%AF%86%E5%88%AB.html&quot;&gt;win10 uwp 使用 OCR 光学字符识别&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E7%9B%B8%E6%9C%BA%E7%9A%84%E5%88%86%E8%BE%A8%E7%8E%87%E8%AE%BE%E7%BD%AE%E6%96%B9%E6%B3%95.html&quot;&gt;win10 uwp 相机的分辨率设置方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%A6%82%E4%BD%95%E4%BF%AE%E6%94%B9-Flyout-%E7%9A%84%E5%AE%BD%E5%BA%A6%E6%88%96%E9%AB%98%E5%BA%A6.html&quot;&gt;win10 uwp 如何修改 Flyout 的宽度或高度&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%BD%BF%E7%94%A8-XamlTreeDump-%E8%8E%B7%E5%8F%96-XAML-%E6%A0%91%E5%85%83%E7%B4%A0%E5%86%85%E5%AE%B9.html&quot;&gt;win10 uwp 使用 XamlTreeDump 获取 XAML 树元素内容&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/UWP-%E5%86%99%E5%85%A5%E5%9B%BE%E7%89%87-Exif-%E4%BF%A1%E6%81%AF.html&quot;&gt;UWP 写入图片 Exif 信息&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%B8%8D%E6%98%BE%E7%A4%BA-SplashScreen-%E6%AC%A2%E8%BF%8E%E7%95%8C%E9%9D%A2%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;win10 uwp 不显示 SplashScreen 欢迎界面的方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-%E7%AD%89%E7%BA%A7%E6%8E%A7%E4%BB%B6.html&quot;&gt;win10 UWP 等级控件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E9%80%9A%E7%9F%A5%E5%88%97%E8%A1%A8.html&quot;&gt;win10 uwp 通知列表&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%BD%BF%E7%94%A8-asp-dotnet-core-%E5%81%9A%E5%9B%BE%E5%BA%8A%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%AE%A2%E6%88%B7%E7%AB%AF.html&quot;&gt;win10 uwp 使用 asp dotnet core 做图床服务器客户端&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E6%98%BE%E7%A4%BASVG.html&quot;&gt;win10 uwp 显示SVG&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E6%B1%82%E4%B8%A4%E4%B8%AA%E7%9F%A9%E5%BD%A2%E7%9B%B8%E8%BF%9E%E7%9A%84%E5%87%A0%E4%BD%95.html&quot;&gt;win10 uwp 求两个矩形相连的几何&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%8E%B7%E5%8F%96%E7%AA%97%E5%8F%A3%E7%9A%84%E5%9D%90%E6%A0%87%E5%92%8C%E5%AE%BD%E5%BA%A6%E9%AB%98%E5%BA%A6.html&quot;&gt;win10 uwp 获取窗口的坐标和宽度高度&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/UWP-%E5%88%86%E4%BA%AB%E7%94%A8%E9%82%A3%E4%B8%AA%E5%9B%BE%E6%A0%87.html&quot;&gt;UWP 分享用那个图标&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8DataTemplate.html&quot;&gt;win10 uwp 如何使用DataTemplate&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E6%89%93%E5%8C%85%E7%AC%AC%E4%B8%89%E6%96%B9%E5%AD%97%E4%BD%93%E5%88%B0%E5%BA%94%E7%94%A8.html&quot;&gt;win10 uwp 打包第三方字体到应用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%8F%B3%E5%87%BB%E6%B5%AE%E5%87%BA%E7%AA%97%E5%9C%A8%E7%82%B9%E5%87%BB%E4%BD%8D%E7%BD%AE.html&quot;&gt;win10 uwp 右击浮出窗在点击位置&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%AF%BB%E5%8F%96%E4%BF%9D%E5%AD%98WriteableBitmap-BitmapImage.html&quot;&gt;win10 uwp 读取保存WriteableBitmap 、BitmapImage&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%B5%84%E6%BA%90%E5%AD%97%E5%85%B8.html&quot;&gt;win10 uwp 资源字典&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-%E5%BA%8F%E5%88%97%E5%8C%96.html&quot;&gt;win10 UWP 序列化&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%A6%82%E4%BD%95%E6%89%93%E5%8C%85Nuget%E7%BB%99%E5%85%B6%E4%BB%96%E4%BA%BA.html&quot;&gt;win10 uwp 如何打包Nuget给其他人&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-%E5%8A%A8%E7%94%BB.html&quot;&gt;win10 UWP 动画&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%BE%9D%E8%B5%96%E5%B1%9E%E6%80%A7.html&quot;&gt;win10 uwp 依赖属性&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-x_Bind-%E6%97%A0%E6%B3%95%E8%8E%B7%E5%BE%97%E8%B5%84%E6%BA%90.html&quot;&gt;win10 uwp x:Bind 无法获得资源&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%A5%BD%E7%9C%8B%E7%9A%84%E6%97%B6%E9%97%B4%E9%80%89%E6%8B%A9%E6%8E%A7%E4%BB%B6.html&quot;&gt;win10 uwp 好看的时间选择控件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%AF%BB%E5%8F%96%E6%96%87%E6%9C%ACGBK%E9%94%99%E8%AF%AF.html&quot;&gt;win10 uwp 读取文本GBK错误&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-%E4%BD%BF%E7%94%A8-MD5%E7%AE%97%E6%B3%95.html&quot;&gt;win10 UWP 使用 MD5算法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%9C%A8%E7%AC%94%E8%BF%B9%E5%BC%80%E5%A7%8B%E4%B9%A6%E5%86%99%E6%8B%BF%E5%88%B0%E4%B9%A6%E5%86%99%E7%A7%BB%E5%8A%A8%E4%BA%8B%E4%BB%B6.html&quot;&gt;win10 uwp 在笔迹开始书写拿到书写移动事件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%A4%84%E7%90%86%E7%94%A8%E6%88%B7%E7%82%B9%E5%87%BB%E5%85%B3%E9%97%AD%E6%8C%89%E9%92%AE.html&quot;&gt;win10 uwp 处理用户点击关闭按钮&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%A7%A3%E5%86%B3-SerialDevice.FromIdAsync-%E8%BF%94%E5%9B%9E%E7%A9%BA.html&quot;&gt;win10 uwp 解决 SerialDevice.FromIdAsync 返回空&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E6%9C%AA%E7%BB%99%E4%BB%BB%E5%8A%A1-GenerateAppxPackageRecipe-%E7%9A%84%E5%BF%85%E9%9C%80%E5%8F%82%E6%95%B0-AppxManifestXml-%E8%B5%8B%E5%80%BC.html&quot;&gt;win10 uwp 未给任务 GenerateAppxPackageRecipe 的必需参数 AppxManifestXml 赋值&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%BC%80%E5%8F%91-CSDN-%E8%AE%BF%E9%97%AE%E9%87%8F%E7%BB%9F%E8%AE%A1-%E6%BA%90%E4%BB%A3%E7%A0%81.html&quot;&gt;win10 uwp 开发 CSDN 访问量统计 源代码&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%BA%94%E7%94%A8%E6%94%BE%E5%88%B0%E6%A1%8C%E9%9D%A2.html&quot;&gt;win10 uwp 应用放到桌面&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%87%AA%E5%AE%9A%E4%B9%89%E6%A0%87%E8%AE%B0%E6%89%A9%E5%B1%95.html&quot;&gt;win10 uwp 自定义标记扩展&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%A6%82%E4%BD%95%E5%B0%86%E5%83%8F%E7%B4%A0%E6%95%B0%E7%BB%84%E8%BD%AC-png-%E6%96%87%E4%BB%B6.html&quot;&gt;win10 uwp 如何将像素数组转 png 文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%A6%82%E4%BD%95%E8%87%AA%E5%AE%9A%E4%B9%89-RichTextBlock-%E5%8F%B3%E9%94%AE%E8%8F%9C%E5%8D%95.html&quot;&gt;win10 uwp 如何自定义 RichTextBlock 右键菜单&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E9%BC%A0%E6%A0%87%E7%A7%BB%E5%8A%A8%E5%88%B0%E5%9B%BE%E7%89%87%E4%B8%8A%E5%88%87%E6%8D%A2%E5%9B%BE%E7%89%87.html&quot;&gt;win10 uwp 鼠标移动到图片上切换图片&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E6%B8%B2%E6%9F%93%E5%8E%9F%E7%90%86-DirectComposition-%E6%B8%B2%E6%9F%93.html&quot;&gt;win10 uwp 渲染原理 DirectComposition 渲染&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%A6%82%E4%BD%95%E5%8F%91%E9%80%81%E7%B1%BB%E5%88%B0-asp-dotnet-core-%E4%BD%9C%E4%B8%BA%E5%8F%82%E6%95%B0.html&quot;&gt;win10 uwp 客户端如何发送类到 asp dotnet core 作为参数&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E7%A6%81%E7%94%A8-ScrollViewer-%E4%BA%A4%E4%BA%92.html&quot;&gt;win10 uwp 禁用 ScrollViewer 交互&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%9C%A8-xaml-%E8%AE%A9-TextBlock-%E6%8D%A2%E8%A1%8C.html&quot;&gt;win10 uwp 在 xaml 让 TextBlock 换行&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;a href=&quot;/post/win10-uwp-%E8%87%AA%E5%AE%9A%E4%B9%89%E6%8E%A7%E4%BB%B6%E5%85%A5%E9%97%A8.html&quot;&gt;win10 uwp 自定义控件入门&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-%E5%89%AA%E8%B4%B4%E6%9D%BF-Clipboard.html&quot;&gt;win10 UWP 剪贴板 Clipboard&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-%E6%A0%87%E9%A2%98%E6%A0%8F%E5%90%8E%E9%80%80.html&quot;&gt;win10 UWP 标题栏后退&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E9%AA%8C%E8%AF%81%E8%BE%93%E5%85%A5-%E8%87%AA%E5%AE%9A%E4%B9%89%E7%94%A8%E6%88%B7%E6%8E%A7%E4%BB%B6.html&quot;&gt;win10 uwp 验证输入 自定义用户控件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E9%87%8D%E5%90%AF%E8%BD%AF%E4%BB%B6.html&quot;&gt;win10 uwp 重启软件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E9%80%80%E5%87%BA%E7%A8%8B%E5%BA%8F.html&quot;&gt;win10 uwp 退出程序&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%BF%9B%E5%BA%A6%E6%9D%A1-WaveProgressControl.html&quot;&gt;win10 uwp 进度条 WaveProgressControl&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%BF%9B%E5%BA%A6%E6%9D%A1-Marquez.html&quot;&gt;win10 uwp 进度条 Marquez &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%AF%BB%E5%86%99XML.html&quot;&gt;win10 uwp 读写XML&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%8E%B7%E5%BE%97%E5%85%83%E7%B4%A0%E7%BB%9D%E5%AF%B9%E5%9D%90%E6%A0%87.html&quot;&gt;win10 uwp 获得元素绝对坐标&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%8E%B7%E5%8F%96%E6%96%87%E4%BB%B6%E5%A4%B9%E5%87%BA%E9%94%99.html&quot;&gt;win10 uwp 获取文件夹出错&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%87%AA%E5%AE%9A%E4%B9%89%E6%8E%A7%E4%BB%B6%E5%88%9D%E5%A7%8B%E5%8C%96.html&quot;&gt;win10 uwp 自定义控件初始化&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E7%BB%98%E5%9B%BE-Line-%E6%8E%A7%E4%BB%B6%E4%BD%BF%E7%94%A8.html&quot;&gt;win10 uwp 绘图  Line 控件使用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E7%BB%91%E5%AE%9A-OneWay-%E6%97%A0%E6%B3%95%E4%BD%BF%E7%94%A8.html&quot;&gt;win10 uwp 绑定 OneWay 无法使用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E7%BA%BF%E7%A8%8B%E6%B1%A0.html&quot;&gt;win10 uwp 线程池&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E7%A6%81%E6%AD%A2%E7%BC%96%E8%AF%91%E5%99%A8%E4%BC%98%E5%8C%96%E4%BB%A3%E7%A0%81.html&quot;&gt;win10 uwp 禁止编译器优化代码&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E6%B0%94%E6%B3%A1.html&quot;&gt;win10 uwp 气泡&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E6%AF%9B%E7%8E%BB%E7%92%83.html&quot;&gt;win10 uwp 毛玻璃&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E6%A8%AA%E5%90%91-AppBarButton.html&quot;&gt;win10 uwp 横向 AppBarButton&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E6%8C%89%E4%B8%8B%E7%AD%89%E5%BE%85%E6%8C%89%E9%92%AE.html&quot;&gt;win10 uwp 按下等待按钮&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E6%8B%96%E5%8A%A8%E6%8E%A7%E4%BB%B6.html&quot;&gt;win10 uwp 拖动控件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E6%89%93%E5%BC%80%E6%96%87%E4%BB%B6%E7%AE%A1%E7%90%86%E5%99%A8%E9%80%89%E6%8B%A9%E6%96%87%E4%BB%B6.html&quot;&gt;win10 uwp 打开文件管理器选择文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%A6%82%E4%BD%95%E8%AE%A9%E4%B8%80%E4%B8%AA%E9%9B%86%E5%90%88%E6%8C%89%E7%85%A7%E9%9C%80%E8%A6%81%E7%9A%84%E9%A1%BA%E5%BA%8F%E8%BF%9B%E8%A1%8C%E6%8E%92%E5%BA%8F.html&quot;&gt;win10 uwp 如何让一个集合按照需要的顺序进行排序&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%A6%82%E4%BD%95%E5%9C%A8DataTemplate%E7%BB%91%E5%AE%9A%E6%96%B9%E6%B3%95.html&quot;&gt;win10 uwp 如何在DataTemplate绑定方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%A6%82%E4%BD%95%E5%88%A4%E6%96%AD%E4%B8%80%E4%B8%AA%E5%AF%B9%E8%B1%A1%E8%A2%AB%E7%A7%BB%E9%99%A4.html&quot;&gt;win10 uwp 如何判断一个对象被移除&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%A6%82%E4%BD%95%E5%88%9B%E5%BB%BA%E4%BF%AE%E6%94%B9%E4%BF%9D%E5%AD%98%E4%BD%8D%E5%9B%BE.html&quot;&gt;win10 uwp 如何创建修改保存位图&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-dataGrid.html&quot;&gt;win10 uwp dataGrid&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-Window.Current.Dispatcher%E4%B8%ADCurrent%E4%B8%BAnull.html&quot;&gt;win10 uwp Window.Current.Dispatcher中Current为null&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-App-to-app-communication-%E5%BA%94%E7%94%A8%E9%80%9A%E4%BF%A1.html&quot;&gt;win10 uwp App-to-app communication 应用通信&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%B0%83%E7%94%A8-Microsoft.Windows.Photos_8wekyb3d8bbwe-%E5%BA%94%E7%94%A8.html&quot;&gt;win10 uwp 调用 Microsoft.Windows.Photos_8wekyb3d8bbwe 应用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-%E5%85%A8%E5%B1%8F.html&quot;&gt;win10 UWP 全屏&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%BD%BF%E7%94%A8%E6%B2%B9%E5%A2%A8%E8%BE%93%E5%85%A5.html&quot;&gt;win10 uwp 使用油墨输入&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%AE%BE%E7%BD%AE-HttpClient-%E6%B5%8F%E8%A7%88%E5%99%A8%E6%A0%87%E8%AF%86.html&quot;&gt;win10 uwp 设置 HttpClient 浏览器标识&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E9%9F%B3%E9%A2%91.html&quot;&gt;win10 uwp 音频&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%AF%BB%E5%86%99csv.html&quot;&gt;win10 uwp 读写csv &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%AE%BE%E7%BD%AE%E5%90%AF%E5%8A%A8%E7%AA%97%E5%8F%A3%E5%A4%A7%E5%B0%8F-%E8%8E%B7%E5%8F%96%E7%AA%97%E5%8F%A3%E5%A4%A7%E5%B0%8F.html&quot;&gt;win10 uwp 设置启动窗口大小  获取窗口大小&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%AE%A9%E7%84%A6%E7%82%B9%E5%9C%A8%E7%82%B9%E5%87%BB%E5%9C%A8%E9%A1%B5%E9%9D%A2%E7%A9%BA%E7%99%BD%E5%A4%84%E6%97%B6%E5%9B%9E%E5%88%B0textbox%E4%B8%AD.html&quot;&gt;win10 uwp 让焦点在点击在页面空白处时回到textbox中&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%8E%B7%E5%BE%97Slider%E6%8B%96%E5%8A%A8%E7%BB%93%E6%9D%9F%E7%9A%84%E5%80%BC.html&quot;&gt;win10 uwp 获得Slider拖动结束的值&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%8E%B7%E5%8F%96%E6%8C%89%E9%92%AE%E9%BC%A0%E6%A0%87%E5%B7%A6%E9%94%AE%E6%8C%89%E4%B8%8B.html&quot;&gt;win10 uwp 获取按钮鼠标左键按下&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%8E%B7%E5%8F%96%E6%8C%87%E5%AE%9A%E7%9A%84%E6%96%87%E4%BB%B6-AQS.html&quot;&gt;win10 uwp 获取指定的文件 AQS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%87%AA%E5%AE%9A%E4%B9%89%E6%8E%A7%E4%BB%B6-SplitViewItem.html&quot;&gt;win10 uwp 自定义控件 SplitViewItem&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B.html&quot;&gt;win10 uwp 网络编程&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E7%BB%91%E5%AE%9A%E9%9D%99%E6%80%81%E5%B1%9E%E6%80%A7.html&quot;&gt;win10 uwp 绑定静态属性&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E7%BB%91%E5%AE%9A%E5%AF%86%E7%A0%81.html&quot;&gt;win10 uwp 绑定密码&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E7%AE%80%E5%8D%95MasterDetail.html&quot;&gt;win10 uwp 简单MasterDetail&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E6%B4%BB%E5%8A%A8%E7%A3%81%E8%B4%B4.html&quot;&gt;win10 uwp 活动磁贴&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E6%94%B9%E5%8F%98%E9%BC%A0%E6%A0%87.html&quot;&gt;win10 uwp 改变鼠标&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E6%89%8B%E5%8A%A8%E9%94%81Bitlocker.html&quot;&gt;win10 uwp 手动锁Bitlocker&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%BC%82%E6%AD%A5%E8%BF%9B%E5%BA%A6%E6%9D%A1.html&quot;&gt;win10 uwp 异步进度条&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%A6%82%E4%BD%95%E8%AE%A9WebView%E6%A0%87%E8%AF%86win10%E6%89%8B%E6%9C%BA.html&quot;&gt;win10 uwp 如何让WebView标识win10手机&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%A6%82%E4%BD%95%E8%AE%A9-Page-%E7%BB%A7%E6%89%BF%E6%B3%9B%E5%9E%8B%E7%B1%BB.html&quot;&gt;win10 uwp 如何让 Page 继承泛型类&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%A6%82%E4%BD%95%E6%8B%96%E5%8A%A8%E4%B8%80%E4%B8%AATextBlock%E7%9A%84%E6%96%87%E5%AD%97%E5%88%B0%E5%8F%A6%E4%B8%80%E4%B8%AATextBlock.html&quot;&gt;win10 uwp 如何拖动一个TextBlock的文字到另一个TextBlock &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%9C%86%E8%A7%92%E6%8C%89%E9%92%AE.html&quot;&gt;win10 uwp 圆角按钮&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%88%A4%E6%96%AD%E6%96%87%E4%BB%B6%E5%AD%98%E5%9C%A8.html&quot;&gt;win10 uwp 判断文件存在&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%88%86%E6%B2%BB%E6%B3%95.html&quot;&gt;win10 uwp 分治法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%BB%8EType%E4%BD%BF%E7%94%A8%E6%9E%84%E9%80%A0.html&quot;&gt;win10 uwp 从Type使用构造&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%BB%8EStorageFile%E8%8E%B7%E5%8F%96%E6%96%87%E4%BB%B6%E5%A4%A7%E5%B0%8F.html&quot;&gt;win10 uwp 从StorageFile获取文件大小&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%B8%8A%E4%BC%A0Nuget-%E8%AE%A9%E5%88%AB%E4%BA%BA%E7%94%A8%E6%88%91%E4%BB%AC%E7%9A%84%E5%BA%93.html&quot;&gt;win10 uwp 上传Nuget 让别人用我们的库&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-iot.html&quot;&gt;win10 uwp iot&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-InkCanvas%E6%8E%A7%E4%BB%B6%E6%95%B0%E6%8D%AE%E7%BB%91%E5%AE%9A.html&quot;&gt;win10 uwp InkCanvas控件数据绑定&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-HttpClient-post%E9%94%99%E8%AF%AF.html&quot;&gt;win10 uwp HttpClient post错误&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-ContentDialog-%E7%82%B9%E7%A1%AE%E5%AE%9A%E4%B8%8D%E5%85%B3%E9%97%AD.html&quot;&gt;win10 uwp ContentDialog 点确定不关闭&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-BadgeLogo-%E9%A2%9C%E8%89%B2.html&quot;&gt;win10 uwp BadgeLogo 颜色&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-%E5%BA%94%E7%94%A8%E8%AE%BE%E7%BD%AE.html&quot;&gt;win10 UWP 应用设置&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95.html&quot;&gt;win10 UWP 单元测试&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-MessageDialog-%E5%92%8C-ContentDialog.html&quot;&gt;win10 UWP MessageDialog 和 ContentDialog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-Hmac.html&quot;&gt;win10 UWP Hmac&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win-10-UWP-%E6%A0%87%E7%AD%BE.html&quot;&gt;win 10 UWP 标签&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Win10-UWP-Intro-to-controls-and-events.html&quot;&gt;Win10 UWP Intro to controls and events&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%8F%82%E8%80%83.html&quot;&gt;win10 uwp 参考&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%BF%AE%E6%94%B9CalendarDatePicker%E5%9B%BE%E6%A0%87%E9%A2%9C%E8%89%B2.html&quot;&gt;win10 uwp 修改CalendarDatePicker图标颜色&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%85%B3%E8%81%94%E6%96%87%E4%BB%B6.html&quot;&gt;win10 uwp 关联文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%8F%91%E5%B8%83%E6%97%81%E5%8A%A0%E8%BD%BD%E8%87%AA%E5%8A%A8%E6%9B%B4%E6%96%B0.html&quot;&gt;win10 uwp 发布旁加载自动更新&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%9C%A8-Grid-%E6%8E%A5%E6%94%B6%E9%94%AE%E7%9B%98%E6%B6%88%E6%81%AF.html&quot;&gt;win10 uwp 在 Grid 接收键盘消息&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%88%87%E6%8D%A2%E4%B8%BB%E9%A2%98.html&quot;&gt;win10 uwp 切换主题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%BD%BF%E7%94%A8-Microsoft.Graph-%E5%8F%91%E9%80%81%E9%82%AE%E4%BB%B6.html&quot;&gt;win10 uwp 使用 Microsoft.Graph 发送邮件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%8A%A8%E7%94%BB%E7%A7%BB%E5%8A%A8%E6%BB%91%E5%8A%A8%E6%9D%A1%E7%9A%84%E6%BB%91%E5%9D%97.html&quot;&gt;win10 uwp 动画移动滑动条的滑块&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/UWP-%E4%BB%8E%E6%96%87%E4%BB%B6-StorageFile-%E8%BD%AC-SoftwareBitmap-%E5%9B%BE%E7%89%87%E6%96%B9%E6%B3%95.html&quot;&gt;UWP 从文件 StorageFile 转 SoftwareBitmap 图片方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%BF%AE%E6%94%B9%E5%9B%BE%E7%89%87%E8%B4%A8%E9%87%8F%E5%8E%8B%E7%BC%A9%E5%9B%BE%E7%89%87.html&quot;&gt;win10 uwp 修改图片质量压缩图片&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%9C%A8-ItemsPanelTemplate-%E9%87%8C%E9%9D%A2%E9%80%9A%E8%BF%87%E6%A0%B7%E5%BC%8F%E7%BB%91%E5%AE%9A-Orientation-%E6%98%BE%E7%A4%BA%E6%96%B9%E5%90%91.html&quot;&gt;win10 uwp 在 ItemsPanelTemplate 里面通过样式绑定 Orientation 显示方向&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/uwp-ScrollViewer-content-out-of-panel-when-set-the-long-width.html&quot;&gt;uwp ScrollViewer content out of panel when set the long width&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%BD%BF%E7%94%A8-ScaleTransform-%E6%94%BE%E5%A4%A7%E6%9F%90%E4%B8%AA%E5%85%83%E7%B4%A0.html&quot;&gt;win10 uwp 使用 ScaleTransform 放大某个元素&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%B8%80%E5%BC%A0%E5%9B%BE%E8%AF%B4%E6%98%8E%E6%B0%B4%E5%B9%B3%E5%AF%B9%E9%BD%90%E5%92%8C%E5%9E%82%E7%9B%B4%E5%AF%B9%E9%BD%90.html&quot;&gt;win10 uwp 一张图说明水平对齐和垂直对齐&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%BD%BF%E7%94%A8-LayoutTransformer.html&quot;&gt;win10 uwp 使用 LayoutTransformer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%8E%BB%E6%8E%89-Flyout-%E8%BE%B9%E6%A1%86.html&quot;&gt;win10 uwp 去掉 Flyout 边框&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-release-%E5%9B%A0%E4%B8%BA-Entry-Point-Not-Found-%E6%97%A0%E6%B3%95%E5%90%AF%E5%8A%A8.html&quot;&gt;win10 uwp release 因为 Entry Point Not Found 无法启动&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%BD%BF%E7%94%A8-Matrix3DProjection-%E8%BF%9B%E8%A1%8C-3d-%E6%8A%95%E5%BD%B1.html&quot;&gt;win10 uwp 使用 Matrix3DProjection 进行 3d 投影&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E6%8E%A7%E4%BB%B6.html&quot;&gt;控件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%B8%89%E7%A7%8D%E6%96%B9%E5%BC%8F%E8%AE%BE%E7%BD%AE%E7%89%B9%E5%AE%9A%E8%AE%BE%E5%A4%87UWP-XAML-view.html&quot;&gt;三种方式设置特定设备UWP XAML view&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%9C%A8-Canvas-%E6%94%BE%E4%B8%80%E4%B8%AA%E8%B6%85%E8%BF%87%E5%A4%A7%E5%B0%8F%E7%9A%84%E5%85%83%E7%B4%A0%E4%BC%9A%E4%B8%8D%E4%BC%9A%E8%A2%AB%E8%A3%81%E5%89%AA.html&quot;&gt;win10 uwp 在 Canvas 放一个超过大小的元素会不会被裁剪&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%90%8E%E5%8F%B0%E8%8E%B7%E5%8F%96%E8%B5%84%E6%BA%90.html&quot;&gt;win10 uwp 后台获取资源&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%8F%8D%E5%B0%84.html&quot;&gt;win10 uwp 反射&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%BF%AE%E6%94%B9Pivot-Header-%E9%A2%9C%E8%89%B2.html&quot;&gt;win10 uwp 修改Pivot Header 颜色&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%BD%BF%E7%94%A8%E8%B5%84%E6%BA%90%E5%9C%A8%E5%90%8E%E5%8F%B0%E5%88%9B%E5%BB%BA%E6%8E%A7%E4%BB%B6.html&quot;&gt;win10 uwp 使用资源在后台创建控件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%BD%BF%E7%94%A8%E5%8A%A8%E7%94%BB%E4%BF%AE%E6%94%B9-Grid-column-%E7%9A%84%E5%AE%BD%E5%BA%A6.html&quot;&gt;win10 uwp 使用动画修改 Grid column 的宽度&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%BD%BF%E7%94%A8-Geometry-resources-%E5%9C%A8-xaml.html&quot;&gt;win10 uwp 使用 Geometry resources 在 xaml&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-slider-%E9%9A%90%E8%97%8F%E6%98%BE%E7%A4%BA%E6%95%B0%E5%80%BC.html&quot;&gt;win10 uwp slider 隐藏显示数值&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-ping.html&quot;&gt;win10 uwp ping&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-json.html&quot;&gt;win10 uwp json&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-httpClient-%E7%99%BB%E9%99%86CSDN.html&quot;&gt;win10 uwp httpClient 登陆CSDN&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-MetroLog-%E5%85%A5%E9%97%A8.html&quot;&gt;win10 uwp MetroLog 入门&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-MVVM-%E8%AF%AD%E4%B9%89%E8%80%A6%E5%90%88.html&quot;&gt;win10 uwp MVVM 语义耦合&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-DataContext.html&quot;&gt;win10 uwp DataContext &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-ApplicationView.html&quot;&gt;win10 uwp ApplicationView&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-sdk-%E6%98%AF%E5%90%A6%E5%90%91%E4%B8%8B%E5%85%BC%E5%AE%B9.html&quot;&gt;win10 sdk 是否向下兼容&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-%E8%AE%BF%E9%97%AE%E7%BD%91%E9%A1%B5.html&quot;&gt;win10 UWP 访问网页&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-%E7%94%A8Path%E7%94%BB%E5%9B%BE.html&quot;&gt;win10 UWP 用Path画图&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-%E5%9C%86%E5%BD%A2%E7%AD%89%E5%BE%85.html&quot;&gt;win10 UWP 圆形等待&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-%E5%8F%91%E9%82%AE%E4%BB%B6.html&quot;&gt;win10 UWP 发邮件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-%E4%BF%AE%E6%94%B9%E5%AF%86%E7%A0%81%E6%A1%86%E6%96%87%E5%AD%97%E6%B0%B4%E5%B9%B3.html&quot;&gt;win10 UWP 修改密码框文字水平&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-button.html&quot;&gt;win10 UWP button&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-ListView.html&quot;&gt;win10 UWP ListView &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-url-encode.html&quot;&gt;win10 uwp url encode&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%BD%BF%E7%94%A8-Border-%E5%B8%83%E5%B1%80.html&quot;&gt;win10 uwp 使用 Border 布局&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-xaml-%E7%BB%91%E5%AE%9A%E6%8E%A5%E5%8F%A3.html&quot;&gt;win10 uwp xaml 绑定接口&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%8A%A8%E6%80%81%E4%BF%AE%E6%94%B9ListView%E5%85%83%E7%B4%A0%E5%B8%83%E5%B1%80.html&quot;&gt;win10 uwp 动态修改ListView元素布局&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%9B%BE%E6%A0%87%E5%88%B6%E4%BD%9C%E5%99%A8.html&quot;&gt;win10 uwp 图标制作器&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%8F%B3%E5%87%BB%E9%80%89%E6%8B%A9-GridViewItem.html&quot;&gt;win10 uwp 右击选择 GridViewItem &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%88%A4%E6%96%AD%E8%AE%BE%E5%A4%87%E7%B1%BB%E5%9E%8B.html&quot;&gt;win10 uwp 判断设备类型&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-unix-timestamp-%E6%97%B6%E9%97%B4%E6%88%B3-%E8%BD%AC-DateTime.html&quot;&gt;win10 uwp unix timestamp 时间戳 转 DateTime&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-hashcash.html&quot;&gt;win10 uwp hashcash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-%E6%98%BE%E7%A4%BA%E5%9C%B0%E5%9B%BE.html&quot;&gt;win10 UWP 显示地图&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-%E4%BD%A0%E5%86%99%E6%88%91%E8%AF%BB.html&quot;&gt;win10 UWP 你写我读&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-%E4%B9%9D%E5%B9%BD%E7%99%BB%E5%BD%95.html&quot;&gt;win10 UWP 九幽登录&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-%E4%B9%9D%E5%B9%BD%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90.html&quot;&gt;win10 UWP 九幽数据分析&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-Markdown-%E5%90%AB%E6%BA%90%E4%BB%A3%E7%A0%81.html&quot;&gt;win10 UWP Markdown 含源代码&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-ListView-%E6%A8%A1%E4%BB%BF%E5%BC%80%E5%A7%8B%E8%8F%9C%E5%8D%95.html&quot;&gt;win10 UWP ListView 模仿开始菜单&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-%E8%9C%98%E8%9B%9B%E7%BD%91%E6%95%88%E6%9E%9C.html&quot;&gt;win10 UWP  蜘蛛网效果&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Win10-%E4%BD%BF%E7%94%A8-GHO-%E5%AE%89%E8%A3%85%E5%87%BA%E7%8E%B0-UWP-%E8%BD%AF%E4%BB%B6%E6%89%93%E5%BC%80%E9%97%AA%E9%80%80-%E5%BA%94%E7%94%A8%E5%95%86%E5%BA%97%E6%97%A0%E6%B3%95%E5%AE%89%E8%A3%85%E8%BD%AF%E4%BB%B6.html&quot;&gt;Win10 使用 GHO 安装出现 UWP 软件打开闪退 应用商店无法安装软件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/UWP-WPF-%E8%A7%A3%E5%86%B3-xaml-%E8%AE%BE%E8%AE%A1%E6%98%BE%E7%A4%BA%E5%BC%82%E5%B8%B8.html&quot;&gt;UWP WPF 解决 xaml 设计显示异常&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E9%9A%90%E7%A7%81%E5%A3%B0%E6%98%8E.html&quot;&gt;win10 uwp 隐私声明&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E9%9A%8F%E7%9D%80%E6%95%B0%E5%AD%97%E5%8F%98%E5%8C%96%E9%A2%9C%E8%89%B2%E6%8E%A7%E4%BB%B6.html&quot;&gt;win10 uwp 随着数字变化颜色控件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%BD%A6%E8%A1%A8%E7%9B%98-%E5%BE%84%E5%90%91%E8%A7%84.html&quot;&gt;win10 uwp 车表盘 径向规&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%8E%B7%E5%BE%97%E7%84%A6%E7%82%B9%E6%94%B9%E5%8F%98.html&quot;&gt;win10 uwp 获得焦点改变&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E6%A0%87%E9%A2%98%E6%A0%8F.html&quot;&gt;win10 uwp 标题栏&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E6%89%93%E7%94%B5%E8%AF%9D.html&quot;&gt;win10 uwp 打电话&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E6%88%AA%E5%9B%BE-%E8%8E%B7%E5%8F%96%E5%B1%8F%E5%B9%95%E6%98%BE%E7%A4%BA%E7%95%8C%E9%9D%A2%E4%BF%9D%E5%AD%98%E5%9B%BE%E7%89%87.html&quot;&gt;win10 uwp 截图 获取屏幕显示界面保存图片&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/UWP-%E8%BD%AC%E6%8D%A2-IBuffer-%E5%92%8C%E5%85%B6%E4%BB%96%E7%B1%BB%E5%9E%8B.html&quot;&gt;UWP 转换 IBuffer 和其他类型&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/How-to-use-code-to-exit-the-application-in-UWP.html&quot;&gt;How to use code to exit the application in UWP&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/UWP-How-to-custom-RichTextBlock-right-click-menu.html&quot;&gt;UWP How to custom RichTextBlock right click menu&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/UWP-IRandomAccessStream-%E4%B8%8E-Stream-%E4%BA%92%E8%BD%AC.html&quot;&gt;UWP IRandomAccessStream 与 Stream 互转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%BC%B9%E8%B5%B7%E9%94%AE%E7%9B%98%E4%B8%8D%E9%9A%90%E8%97%8F%E7%95%8C%E9%9D%A2%E5%85%83%E7%B4%A0.html&quot;&gt;win10 uwp 弹起键盘不隐藏界面元素&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%BA%94%E7%94%A8%E8%BD%AC%E5%90%8E%E5%8F%B0%E6%B8%85%E7%90%86%E5%86%85%E5%AD%98.html&quot;&gt;win10 uwp 应用转后台清理内存&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%B8%83%E5%B1%80.html&quot;&gt;win10 uwp 布局&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%AD%98%E6%94%BE%E7%BD%91%E7%BB%9C%E5%9B%BE%E7%89%87%E5%88%B0%E6%9C%AC%E5%9C%B0.html&quot;&gt;win10 uwp 存放网络图片到本地&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%88%87%E6%8D%A2%E4%B8%BB%E9%A2%98.html&quot;&gt;win10 uwp 切换主题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%85%B4%E8%B6%A3%E7%BA%BF.html&quot;&gt;win10 uwp 兴趣线&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%BF%9D%E5%AD%98%E7%94%A8%E6%88%B7%E9%80%89%E6%8B%A9%E6%96%87%E4%BB%B6%E5%A4%B9.html&quot;&gt;win10 uwp 保存用户选择文件夹&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%B9%9D%E5%B9%BD%E5%9B%BE%E5%BA%8A.html&quot;&gt;win10 uwp 九幽图床&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-Markdown.html&quot;&gt;win10 uwp Markdown&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E7%94%A8%E5%B9%BF%E5%91%8A%E8%B5%9A%E9%92%B1.html&quot;&gt;win10 uwp 用广告赚钱&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-Fluent-Design-System-%E5%AE%9E%E8%B7%B5.html&quot;&gt;win10 uwp Fluent Design System 实践&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-smms%E5%9B%BE%E5%BA%8A.html&quot;&gt;win10 uwp smms图床&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/UWP-WinUI3-%E4%BC%A0%E5%85%A5-AddHandler-%E7%9A%84-RoutedEventHandler-%E7%B1%BB%E5%9E%8B%E4%B8%8E%E4%BA%8B%E4%BB%B6%E6%89%80%E9%9C%80%E4%B8%8D%E5%8C%B9%E9%85%8D%E5%B0%86%E6%8A%9B%E5%87%BA%E5%8F%82%E6%95%B0%E5%BC%82%E5%B8%B8.html&quot;&gt;UWP WinUI3 传入 AddHandler 的 RoutedEventHandler 类型与事件所需不匹配将抛出参数异常&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;mvvm&quot;&gt;MVVM&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-MvvmLight%E5%85%A5%E9%97%A8.html&quot;&gt;win10 UWP MvvmLight入门&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-MVVM%E5%85%A5%E9%97%A8.html&quot;&gt;win10 uwp MVVM入门&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-MVVM-%E8%BD%BB%E9%87%8F%E6%A1%86%E6%9E%B6.html&quot;&gt;win10 uwp MVVM 轻量框架&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%BD%BB%E9%87%8F%E7%BA%A7-MVVM-%E6%A1%86%E6%9E%B6%E5%85%A5%E9%97%A8-2.1.5.3199.html&quot;&gt;win10 uwp 轻量级 MVVM 框架入门 2.1.5.3199&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;构建部署&quot;&gt;构建部署&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%8F%91%E5%B8%83%E7%9A%84%E6%97%B6%E5%80%99-ILC-%E7%BC%96%E8%AF%91%E4%B8%8D%E9%80%9A%E8%BF%87.html&quot;&gt;win10 uwp 发布的时候 ILC 编译不通过&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/UWP-%E9%83%A8%E7%BD%B2%E5%A4%B1%E8%B4%A5-DEP0700-0x80073CF0-%E9%94%99%E8%AF%AF-0x800701C0-%E4%BB%8E%E4%BD%8D%E7%BD%AE-AppxManifest.xml-%E4%B8%AD%E6%89%93%E5%BC%80%E6%96%87%E4%BB%B6%E5%A4%B1%E8%B4%A5.html&quot;&gt;UWP 部署失败 DEP0700 0x80073CF0 错误 0x800701C0 从位置 AppxManifest.xml 中打开文件失败&lt;/a&gt;
&lt;!-- [UWP 部署失败 DEP0700 0x80073CF0 错误 0x800701C0 从位置 AppxManifest.xml 中打开文件失败 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18676067 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%BD%BF%E7%94%A8-AppCenter-%E8%87%AA%E5%8A%A8%E6%9E%84%E5%BB%BA.html&quot;&gt;win10 uwp 使用 AppCenter 自动构建&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%BD%BF%E7%94%A8-Azure-DevOps-%E8%87%AA%E5%8A%A8%E6%9E%84%E5%BB%BA.html&quot;&gt;win10 uwp 使用 Azure DevOps 自动构建&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%9C%A8-VisualStudio-%E9%83%A8%E7%BD%B2%E5%A4%B1%E8%B4%A5-%E6%89%BE%E4%B8%8D%E5%88%B0-Windows-Phone-%E5%8F%AF%E8%83%BD%E7%9A%84%E5%8E%9F%E5%9B%A0.html&quot;&gt;win10 uwp 在 VisualStudio 部署失败，找不到 Windows Phone 可能的原因&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/UWP-%E6%89%93%E5%8C%85-win32-%E5%BA%94%E7%94%A8-%E6%B7%BB%E5%8A%A0%E9%98%B2%E7%81%AB%E5%A2%99%E4%BE%8B%E5%A4%96.html&quot;&gt;UWP 打包 win32 应用 添加防火墙例外&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%89%93%E5%8C%85%E4%B8%BA-UWP-%E5%BA%94%E7%94%A8%E6%9E%84%E5%BB%BA%E5%A4%B1%E8%B4%A5-MSB3270-%E4%B8%8D%E5%8C%B9%E9%85%8D-AMD64-%E6%9E%B6%E6%9E%84.html&quot;&gt;WPF 打包为 UWP 应用构建失败 MSB3270 不匹配 AMD64 架构&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%BD%BF%E7%94%A8-msbuild-%E5%91%BD%E4%BB%A4%E8%A1%8C%E7%BC%96%E8%AF%91-UWP-%E7%A8%8B%E5%BA%8F.html&quot;&gt;win10 uwp 使用 msbuild 命令行编译 UWP 程序&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BF%AE%E5%A4%8D-WPF-%E5%AE%89%E8%A3%85-WindowsAppSDK-%E5%BA%93%E6%9E%84%E5%BB%BA%E5%A4%B1%E8%B4%A5-NETSDK1082-%E5%92%8C-NETSDK1112-%E6%89%BE%E4%B8%8D%E5%88%B0-win10-arm-%E5%A4%B1%E8%B4%A5.html&quot;&gt;修复 WPF 安装 WindowsAppSDK 库构建失败 NETSDK1082 和 NETSDK1112 找不到 win10-arm 失败&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;发布&quot;&gt;发布&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%A6%82%E4%BD%95%E5%9C%A8%E5%9B%BD%E5%86%85%E5%8F%91%E5%B8%83-UWP-%E5%BA%94%E7%94%A8.html&quot;&gt;如何在国内发布 UWP 应用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%8A%A0%E5%BC%BA%E7%89%88%E5%9C%A8%E5%9B%BD%E5%86%85%E5%88%86%E5%8F%91-UWP-%E5%BA%94%E7%94%A8%E6%AD%A3%E7%A1%AE%E6%96%B9%E5%BC%8F-%E9%80%9A%E8%BF%87win32%E5%AE%89%E8%A3%85UWP%E5%BA%94%E7%94%A8.html&quot;&gt;加强版在国内分发 UWP 应用正确方式 通过win32安装UWP应用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/UWP-%E4%B8%8A%E6%9E%B6%E5%A4%B1%E8%B4%A5%E5%9B%A0%E4%B8%BA%E6%B2%A1%E6%9C%89%E6%B7%BB%E5%8A%A0%E9%9A%90%E7%A7%81%E7%AD%96%E7%95%A5.html&quot;&gt;UWP 上架失败因为没有添加隐私策略&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%9C%A8-VisualStudio-%E6%89%93%E5%8C%85%E6%B5%8B%E8%AF%95%E5%AE%8C%E6%88%90%E8%87%AA%E5%8A%A8%E4%B8%8A%E4%BC%A0%E5%88%B0%E5%BA%94%E7%94%A8%E5%95%86%E5%BA%97.html&quot;&gt;win10 uwp 在 VisualStudio 打包测试完成自动上传到应用商店&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%BA%94%E7%94%A8%E5%8C%85%E4%B8%8A%E4%BC%A0%E5%A4%B1%E8%B4%A5%E6%97%A0%E6%95%88%E7%9A%84%E8%BD%AF%E4%BB%B6%E5%8C%85%E7%B3%BB%E5%88%97%E5%90%8D%E7%A7%B0.html&quot;&gt;win10 uwp 应用包上传失败无效的软件包系列名称&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%AE%89%E8%A3%85%E6%96%87%E4%BB%B6-appinstaller-%E6%A0%BC%E5%BC%8F.html&quot;&gt;win10 uwp 安装文件 appinstaller 格式&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;调试&quot;&gt;调试&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%B0%83%E8%AF%95%E8%BD%AF%E4%BB%B6%E5%90%AF%E5%8A%A8.html&quot;&gt;win10 uwp 调试软件启动&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E9%9A%90%E8%97%8F%E5%AE%9E%E6%97%B6%E5%8F%AF%E8%A7%86%E5%8C%96.html&quot;&gt;win10 uwp 隐藏实时可视化&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E6%97%A0%E6%B3%95%E9%99%84%E5%8A%A0%E5%88%B0CoreCLR.html&quot;&gt;win10 uwp 无法附加到CoreCLR&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E4%BD%BF%E7%94%A8-WinDbg-%E8%B0%83%E8%AF%95.html&quot;&gt;win10 uwp 使用 WinDbg 调试&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;应用开发&quot;&gt;应用开发&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BF%84%E7%BD%97%E6%96%AF%E6%96%B9%E5%9D%97.html&quot;&gt;俄罗斯方块&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%95%86%E4%B8%9A%E6%B8%B8%E6%88%8F.html&quot;&gt;win10 uwp 商业游戏 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%95%86%E4%B8%9A%E6%B8%B8%E6%88%8F-1.1.5.html&quot;&gt;win10 uwp 商业游戏 1.1.5&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E5%95%86%E4%B8%9A%E6%B8%B8%E6%88%8F-1.2.1.html&quot;&gt;win10 uwp 商业游戏 1.2.1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E8%A3%85%E6%9C%BA%E5%BF%85%E5%A4%87%E5%BA%94%E7%94%A8-%E5%90%AB%E6%BA%90%E4%BB%A3%E7%A0%81.html&quot;&gt;win10 uwp 装机必备应用 含源代码&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-RSS%E9%98%85%E8%AF%BB%E5%99%A8.html&quot;&gt;win10 UWP RSS阅读器&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-csdn-%E5%8D%9A%E5%AE%A2%E9%98%85%E8%AF%BB%E5%99%A8.html&quot;&gt;win10 uwp csdn 博客阅读器&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E7%A7%81%E5%AF%86%E5%AF%86%E7%A0%81%E6%9C%AC-2.1-%E6%BA%90%E4%BB%A3%E7%A0%81.html&quot;&gt;私密密码本 2.1 源代码&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;win2d&quot;&gt;Win2D&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-win2d-%E7%A6%BB%E5%B1%8F%E6%B8%B2%E6%9F%93.html&quot;&gt;win10 uwp win2d 离屏渲染&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win2d-CanvasRenderTarget-vs-CanvasBitmap.html&quot;&gt;win2d CanvasRenderTarget vs CanvasBitmap&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-win2d-%E4%BD%BF%E7%94%A8-Path-%E7%BB%98%E5%88%B6%E7%95%8C%E9%9D%A2.html&quot;&gt;win10 uwp win2d 使用 Path 绘制界面&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-win2d-CanvasVirtualControl-%E4%B8%8E-CanvasAnimatedControl.html&quot;&gt;win10 uwp win2d CanvasVirtualControl 与 CanvasAnimatedControl&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E9%80%9A%E8%BF%87-win2d-%E7%94%BB%E5%87%BA%E7%AC%94%E8%BF%B9.html&quot;&gt;win10 uwp 通过 win2d 画出笔迹&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-%E9%80%9A%E8%BF%87-Win2d-%E5%AE%8C%E5%85%A8%E6%8E%A7%E5%88%B6%E7%AC%94%E8%BF%B9%E7%BB%98%E5%88%B6%E9%80%BB%E8%BE%91.html&quot;&gt;win10 uwp 通过 Win2d 完全控制笔迹绘制逻辑&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-uwp-win2d-%E7%89%B9%E6%95%88.html&quot;&gt;win10 uwp win2d 特效&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win2d-%E9%80%9A%E8%BF%87-CanvasActiveLayer-%E7%94%BB%E5%87%BA%E9%80%8F%E6%98%8E%E5%BA%A6%E5%92%8C%E8%A3%81%E5%89%AA.html&quot;&gt;win2d 通过 CanvasActiveLayer 画出透明度和裁剪&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win2d-%E5%9B%BE%E7%89%87%E6%B0%B4%E5%8D%B0.html&quot;&gt;win2d 图片水印&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;

&lt;p&gt;&lt;a href=&quot;/post/win2d-%E7%94%BB%E5%87%BA%E5%A5%BD%E7%9C%8B%E7%9A%84%E5%9B%BE%E5%BD%A2.html&quot;&gt;win2d 画出好看的图形&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win2d-%E6%B8%90%E5%8F%98%E9%A2%9C%E8%89%B2.html&quot;&gt;win2d 渐变颜色&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-WinUI3-Win2D-%E7%BF%BB%E8%BD%AC%E5%9B%BE%E7%89%87.html&quot;&gt;dotnet WinUI3 Win2D 翻转图片&lt;/a&gt;
&lt;!-- [dotnet WinUI3 Win2D 翻转图片 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18288633 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E4%BB%8E%E6%8E%A7%E5%88%B6%E5%8F%B0%E5%BC%80%E5%A7%8B-%E5%85%B3%E8%81%94-Win2D-%E5%92%8C-WinUI-3-%E5%BA%94%E7%94%A8.html&quot;&gt;dotnet C# 从控制台开始 关联 Win2D 和 WinUI 3 应用&lt;/a&gt;
&lt;!-- [dotnet C# 从控制台开始 关联 Win2D 和 WinUI 3 应用 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18378612 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;winui-3&quot;&gt;WinUI 3&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E4%BB%8E%E6%8E%A7%E5%88%B6%E5%8F%B0%E5%88%9B%E5%BB%BA-WinUI-3-%E5%BA%94%E7%94%A8.html&quot;&gt;C# 从控制台创建 WinUI 3 应用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WinUI-3-%E4%BF%AE%E5%A4%8D%E9%9D%9E%E6%89%93%E5%8C%85%E5%BA%94%E7%94%A8%E8%BF%90%E8%A1%8C%E6%8F%90%E7%A4%BA%E7%BC%BA%E5%B0%91-Windows-App-Runtime-%E7%8E%AF%E5%A2%83.html&quot;&gt;WinUI 3 修复非打包应用运行提示缺少 Windows App Runtime 环境&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-WinUI-3-%E4%BF%AE%E5%A4%8D%E9%9D%9E%E6%89%93%E5%8C%85%E5%BA%94%E7%94%A8%E8%BF%90%E8%A1%8C%E6%8F%90%E7%A4%BA-Microsoft.ui.xaml.dll-%E6%89%BE%E4%B8%8D%E5%88%B0.html&quot;&gt;dotnet WinUI 3 修复非打包应用运行提示 Microsoft.ui.xaml.dll 找不到&lt;/a&gt;
&lt;!-- [dotnet WinUI 3 修复非打包应用运行提示 Microsoft.ui.xaml.dll 找不到 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18381793 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E7%AE%80%E5%8D%95%E5%9C%A8-WinUI-%E4%BB%BF%E9%80%A0-WPF-%E7%9A%84-ColumnDefinition-SharedSizeGroup-%E5%85%B1%E4%BA%AB%E5%88%97%E5%AE%BD%E5%8A%9F%E8%83%BD.html&quot;&gt;简单在 WinUI 仿造 WPF 的 ColumnDefinition SharedSizeGroup 共享列宽功能&lt;/a&gt;
&lt;!-- [简单在 WinUI 仿造 WPF 的 ColumnDefinition SharedSizeGroup 共享列宽功能 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18353046 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-WinUI3-Win2D-%E7%BF%BB%E8%BD%AC%E5%9B%BE%E7%89%87.html&quot;&gt;dotnet WinUI3 Win2D 翻转图片&lt;/a&gt;
&lt;!-- [dotnet WinUI3 Win2D 翻转图片 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18288633 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E4%BB%8E%E6%8E%A7%E5%88%B6%E5%8F%B0%E5%BC%80%E5%A7%8B-%E5%85%B3%E8%81%94-Win2D-%E5%92%8C-WinUI-3-%E5%BA%94%E7%94%A8.html&quot;&gt;dotnet C# 从控制台开始 关联 Win2D 和 WinUI 3 应用&lt;/a&gt;
&lt;!-- [dotnet C# 从控制台开始 关联 Win2D 和 WinUI 3 应用 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18378612 ) --&gt;&lt;/p&gt;

&lt;h2 id=&quot;winforms&quot;&gt;WinForms&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/post/WinForms-%E4%BD%BF%E7%94%A8-Image-%E7%9A%84-FromFile-%E6%96%B9%E6%B3%95%E5%8A%A0%E8%BD%BD%E6%96%87%E4%BB%B6%E5%92%8C%E4%BD%BF%E7%94%A8-Bitmap-%E6%9C%89%E4%BB%80%E4%B9%88%E4%B8%8D%E5%90%8C.html&quot;&gt;WinForms 使用 Image 的 FromFile 方法加载文件和使用 Bitmap 有什么不同&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WinForms-%E4%B8%8B%E7%9A%84%E9%AB%98%E6%80%A7%E8%83%BD%E7%AC%94%E8%BF%B9%E6%96%B9%E6%B3%95.html&quot;&gt;WinForms 下的高性能笔迹方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Winforms-%E5%8F%AF%E8%83%BD%E9%81%87%E5%88%B0%E7%9A%84-1000-%E4%B8%AA%E9%97%AE%E9%A2%98.html&quot;&gt;Winforms 可能遇到的 1000 个问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-winforms-%E8%BE%93%E5%85%A5%E9%A2%9C%E8%89%B2%E8%BD%AC%E6%8D%A2%E9%A2%9C%E8%89%B2%E5%90%8D.html&quot;&gt;C# winforms 输入颜色转换颜色名&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;maui&quot;&gt;MAUI&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-Multi-platform-App-UI-%E5%A4%9A%E5%B9%B3%E5%8F%B0%E5%BA%94%E7%94%A8-UI-%E6%A1%86%E6%9E%B6%E7%AE%80%E4%BB%8B.html&quot;&gt;dotnet Multi-platform App UI 多平台应用 UI 框架简介&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/MAUI-%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BB%98%E5%9B%BE%E5%85%A5%E9%97%A8.html&quot;&gt;MAUI 自定义绘图入门&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-MAUI-%E7%9A%84%E8%87%AA%E7%BB%98%E5%88%B6%E9%80%BB%E8%BE%91.html&quot;&gt;WPF 使用 MAUI 的自绘制逻辑&lt;/a&gt; &lt;a href=&quot;https://www.cnblogs.com/lindexi/p/16395472.html&quot;&gt;博客园&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%8E%A7%E5%88%B6%E5%8F%B0-%E4%BD%BF%E7%94%A8-Microsoft.Maui.Graphics-%E9%85%8D%E5%90%88-Skia-%E8%BF%9B%E8%A1%8C%E7%BB%98%E5%9B%BE%E5%85%A5%E9%97%A8.html&quot;&gt;dotnet 控制台 使用 Microsoft.Maui.Graphics 配合 Skia 进行绘图入门&lt;/a&gt; &lt;a href=&quot;https://www.cnblogs.com/lindexi/p/16433320.html&quot;&gt;博客园&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Microsoft.Maui.Graphics.Skia-%E4%BD%BF%E7%94%A8-DrawString-%E7%BB%98%E5%88%B6%E6%96%87%E6%9C%AC%E7%9A%84%E5%9D%90%E6%A0%87%E9%97%AE%E9%A2%98.html&quot;&gt;Microsoft.Maui.Graphics.Skia 使用 DrawString 绘制文本的坐标问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/zh7791/p/15594282.html&quot;&gt;MAUI中使用Maui.Graphics.Controls绘制控件 - 痕迹g - 博客园&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-OpenXML-%E4%BD%BF%E7%94%A8-MAUI-%E6%B8%B2%E6%9F%93-PPT-%E7%9A%84%E9%9D%A2%E7%A7%AF%E5%9B%BE%E5%9B%BE%E8%A1%A8.html&quot;&gt;dotnet OpenXML 使用 MAUI 渲染 PPT 的面积图图表&lt;/a&gt; &lt;a href=&quot;https://www.cnblogs.com/lindexi/p/16671495.html&quot;&gt;博客园&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/MAUI-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-PathFigureCollectionConverter-%E9%9D%9E%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8.html&quot;&gt;MAUI 已知问题 PathFigureCollectionConverter 非线程安全&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;maui-框架开发&quot;&gt;MAUI 框架开发&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/MAUI-%E6%A1%86%E6%9E%B6%E5%BC%80%E5%8F%91-%E5%B0%86-MAUI-%E5%B5%8C%E5%85%A5%E5%88%B0-WPF-%E6%8E%A7%E4%BB%B6%E9%87%8C.html&quot;&gt;MAUI 框架开发 将 MAUI 嵌入到 WPF 控件里&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AF%BB-MAUI-%E6%BA%90%E4%BB%A3%E7%A0%81-%E7%90%86%E8%A7%A3%E5%8F%AF%E7%BB%91%E5%AE%9A%E5%AF%B9%E8%B1%A1%E5%92%8C%E5%8F%AF%E7%BB%91%E5%AE%9A%E5%B1%9E%E6%80%A7%E7%9A%84%E5%AD%98%E5%82%A8%E6%9C%BA%E5%88%B6.html&quot;&gt;读 MAUI 源代码 理解可绑定对象和可绑定属性的存储机制&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;xamarin&quot;&gt;Xamarin&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/post/Xamarin.Forms-%E9%80%89%E5%8F%96%E6%96%87%E4%BB%B6-%E8%AE%A9%E7%94%A8%E6%88%B7%E9%80%89%E6%8B%A9%E6%9C%AC%E5%9C%B0%E6%96%87%E4%BB%B6.html&quot;&gt;Xamarin.Forms 选取文件 让用户选择本地文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Xamarin-iOS-%E9%83%A8%E7%BD%B2%E5%BA%94%E7%94%A8%E6%8F%90%E7%A4%BA-iOS-code-signing-key-%E5%A4%B1%E8%B4%A5.html&quot;&gt;Xamarin iOS 部署应用提示 iOS code signing key 失败&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Xamarin-%E5%92%8C-WPF-%E7%9A%84%E6%8E%A7%E4%BB%B6%E5%92%8C%E5%B1%9E%E6%80%A7%E7%9A%84%E6%9B%BF%E6%8D%A2.html&quot;&gt;Xamarin 和 WPF 的控件和属性的替换&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Xamarin-%E6%9E%84%E5%BB%BA%E5%AE%89%E5%8D%93%E5%A4%B1%E8%B4%A5-%E5%9B%A0%E4%B8%BA%E8%B7%AF%E5%BE%84%E5%A4%AA%E9%95%BF.html&quot;&gt;Xamarin 构建安卓失败 因为路径太长&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Xamarin-%E6%9E%84%E5%BB%BA%E6%8F%90%E7%A4%BA-error-APT2260-resource-%E6%89%BE%E4%B8%8D%E5%88%B0%E8%B5%84%E6%BA%90.html&quot;&gt;Xamarin 构建提示 error APT2260 resource 找不到资源&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Xamarin.Forms-%E6%8C%89%E9%92%AE%E6%A0%B7%E5%BC%8F-%E5%9C%86%E8%A7%92%E6%8C%89%E9%92%AE.html&quot;&gt;Xamarin.Forms 按钮样式 圆角按钮&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Xamarin-%E8%A7%A3%E5%86%B3%E5%88%9B%E5%BB%BA%E7%A9%BA%E7%99%BD%E9%A1%B9%E7%9B%AE%E7%BC%96%E8%AF%91%E6%8F%90%E7%A4%BA-linking-references-%E6%89%BE%E4%B8%8D%E5%88%B0%E8%B5%84%E6%BA%90.html&quot;&gt;Xamarin 解决创建空白项目编译提示 linking references 找不到资源&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Xamarin-Forms-WPF-%E5%B9%B2%E6%8E%89%E9%BB%98%E8%AE%A4%E7%9A%84%E7%AA%97%E5%8F%A3%E5%AF%BC%E8%88%AA%E6%9D%A1.html&quot;&gt;Xamarin Forms WPF 干掉默认的窗口导航条&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Xamarin-iOS-%E9%83%A8%E7%BD%B2%E7%89%88%E6%9C%AC%E5%A4%A7%E4%BA%8E%E6%89%8B%E6%9C%BA%E7%9A%84%E7%B3%BB%E7%BB%9F%E7%89%88%E6%9C%AC.html&quot;&gt;Xamarin iOS 部署版本大于手机的系统版本&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Xamarin-XamlCTask-%E4%BB%BB%E5%8A%A1%E4%B8%8D%E6%94%AF%E6%8C%81-ValidateOnly-%E5%8F%82%E6%95%B0.html&quot;&gt;Xamarin XamlCTask 任务不支持 ValidateOnly 参数&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Xamarin-%E4%BD%BF%E7%94%A8-GTK-%E6%8F%90%E7%A4%BA%E6%89%BE%E4%B8%8D%E5%88%B0-libglib-2.0-0.dll-%E6%89%BE%E4%B8%8D%E5%88%B0.html&quot;&gt;Xamarin 使用 GTK 提示找不到 libglib-2.0-0.dll 找不到&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Xamarin-%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E9%83%A8%E7%BD%B2-iOS-%E4%B8%8A%E7%9A%84-Walterlv.CloudKeyboard-%E5%BA%94%E7%94%A8.html&quot;&gt;Xamarin 从零开始部署 iOS 上的 Walterlv.CloudKeyboard 应用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Xamarin-Forms-%E8%BF%9B%E5%BA%A6%E6%9D%A1%E6%8E%A7%E4%BB%B6.html&quot;&gt;Xamarin Forms 进度条控件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Xamarin-Forms-4.7-%E6%94%AF%E6%8C%81%E6%9B%B4%E7%AE%80%E5%8C%96%E7%9A%84-Grid-%E8%A1%8C%E5%88%97%E5%B8%83%E5%B1%80%E5%86%99%E6%B3%95.html&quot;&gt;Xamarin Forms 4.7 支持更简化的 Grid 行列布局写法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Xamarin-Forms-%E6%9E%84%E5%BB%BA-WPF-%E7%89%88%E9%A1%B9%E7%9B%AE%E5%A4%B1%E8%B4%A5%E6%8F%90%E7%A4%BA-XamlC-error-XFC0000-%E9%94%99%E8%AF%AF.html&quot;&gt;Xamarin Forms 构建 WPF 版项目失败提示 XamlC error XFC0000 错误&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BB%8E%E9%9B%B6%E6%89%8B%E5%8A%A8%E5%88%9B%E5%BB%BA%E6%89%BF%E8%BD%BD-Xamarin-Forms-%E9%A1%B9%E7%9B%AE.html&quot;&gt;WPF 从零手动创建承载 Xamarin Forms 项目&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Mac-%E5%8D%87%E7%BA%A7%E5%88%B0-dotnet-5-%E6%9E%84%E5%BB%BA-Xamarin-%E5%BA%94%E7%94%A8%E5%A4%B1%E8%B4%A5-error-MSB4186-%E9%9D%99%E6%80%81%E6%96%B9%E6%B3%95%E8%B0%83%E7%94%A8%E8%AF%AD%E6%B3%95%E6%97%A0%E6%95%88.html&quot;&gt;Mac 升级到 dotnet 5 构建 Xamarin 应用失败 error MSB4186 静态方法调用语法无效&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;unity3d&quot;&gt;Unity3D&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/post/Unity3d-%E5%85%A5%E9%97%A8-%E6%8E%A7%E5%88%B6%E7%9B%B8%E6%9C%BA%E7%A7%BB%E5%8A%A8.html&quot;&gt;Unity3d 入门 控制相机移动&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Unity3D-%E7%94%A8%E5%AF%B9%E8%B1%A1%E5%88%9B%E5%BB%BA%E5%AF%B9%E8%B1%A1.html&quot;&gt;Unity3D 用对象创建对象&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Unity3D-%E5%88%A4%E6%96%AD%E7%82%B9%E5%87%BB%E5%91%BD%E4%B8%AD%E7%89%A9%E4%BD%93%E5%AF%B9%E8%B1%A1%E5%92%8C%E5%91%BD%E4%B8%AD%E7%8E%AF%E5%A2%83.html&quot;&gt;Unity3D 判断点击命中物体对象和命中环境&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Unity3d-%E4%BF%AE%E6%94%B9%E7%BC%96%E8%BE%91%E7%95%8C%E9%9D%A2%E6%8E%A7%E5%88%B6%E5%9B%BE%E6%A0%87%E5%A4%A7%E5%B0%8F-%E4%BF%AE%E6%94%B9%E7%9B%B8%E6%9C%BA%E5%9B%BE%E6%A0%87%E6%98%BE%E7%A4%BA%E5%A4%A7%E5%B0%8F.html&quot;&gt;Unity3d 修改编辑界面控制图标大小 修改相机图标显示大小&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Unity3d-%E8%BF%9E%E7%BB%AD%E6%8C%89%E9%94%AE%E5%A4%84%E7%90%86%E5%92%8C%E5%8D%95%E6%AC%A1%E6%8C%89%E9%94%AE%E5%A4%84%E7%90%86.html&quot;&gt;Unity3d 连续按键处理和单次按键处理&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Unity3d-%E5%88%B6%E4%BD%9C%E4%B8%80%E4%B8%AA%E7%AB%8B%E4%BD%93%E6%97%8B%E8%BD%AC%E7%9B%B8%E5%86%8C.html&quot;&gt;Unity3d 制作一个立体旋转相册&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Unity3D-2019-%E8%AE%BE%E7%BD%AE%E7%9A%84-Playmode-tint-%E6%9C%89%E4%BD%95%E4%BD%9C%E7%94%A8.html&quot;&gt;Unity3D 2019 设置的 Playmode tint 有何作用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Unity3d-2019.3-%E9%80%9A%E8%BF%87-Ctrl-%E9%94%AE%E8%AE%A9%E7%A7%BB%E5%8A%A8%E6%98%AF%E5%9B%BA%E5%AE%9A%E6%AD%A5%E8%B7%9D.html&quot;&gt;Unity3d 2019.3 通过 Ctrl 键让移动是固定步距&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Unity-2019.3-%E5%B0%86-RenderTexture-%E7%BB%91%E5%AE%9A%E5%88%B0%E7%9B%B8%E6%9C%BA%E5%92%8C%E7%89%A9%E4%BD%93%E4%BD%9C%E5%87%BA%E9%95%9C%E5%AD%90%E6%95%88%E6%9E%9C.html&quot;&gt;Unity 2019.3 将 RenderTexture 绑定到相机和物体作出镜子效果&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;vr&quot;&gt;VR&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/Unity3D-OpenVR-SteamVR-%E5%9C%A8%E5%A4%B4%E7%9B%94%E8%A7%86%E8%A7%89%E5%89%8D%E9%9D%A2%E5%B8%B8%E9%A9%BB%E6%96%87%E6%9C%AC.html&quot;&gt;Unity3D OpenVR SteamVR 在头盔视觉前面常驻文本&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Unity3D-OpenVR-SteamVR-%E7%82%B9%E5%87%BB%E8%8F%9C%E5%8D%95%E5%88%87%E6%8D%A2%E5%9C%BA%E6%99%AF.html&quot;&gt;Unity3D OpenVR SteamVR 点击菜单切换场景&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Unity3D-OpenVR-SteamVR-%E8%8E%B7%E5%8F%96%E8%BE%93%E5%85%A5%E5%8A%A8%E4%BD%9C%E6%8C%89%E9%94%AE-%E4%BA%A4%E4%BA%92%E8%AE%BE%E5%A4%87%E6%95%B0%E6%8D%AE%E6%96%B9%E6%B3%95.html&quot;&gt;Unity3D OpenVR SteamVR 获取输入动作按键 交互设备数据方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;a href=&quot;/post/Unity3D-OpenVR-%E8%99%9A%E6%8B%9F%E7%8E%B0%E5%AE%9E-%E4%BF%9D%E9%BE%84%E7%90%83%E6%89%93%E7%A0%96%E5%9D%97%E6%B8%B8%E6%88%8F%E5%BC%80%E5%8F%91.html&quot;&gt;Unity3D OpenVR 虚拟现实 保龄球打砖块游戏开发&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;ai&quot;&gt;AI&lt;/h2&gt;

&lt;h3 id=&quot;microsoft-agent-framework&quot;&gt;Microsoft Agent Framework&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/Microsoft-Agent-Framework-%E4%B8%8E-DeepSeek-%E5%AF%B9%E6%8E%A5.html&quot;&gt;Microsoft Agent Framework 与 DeepSeek 对接&lt;/a&gt;
&lt;!-- [Microsoft Agent Framework 与 DeepSeek 对接 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19413475 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-Microsoft-Agent-Framework-%E4%B8%8E-%E8%B1%86%E5%8C%85-%E5%AF%B9%E6%8E%A5.html&quot;&gt;C# Microsoft Agent Framework 与 豆包 对接&lt;/a&gt;
&lt;!-- [C# Microsoft Agent Framework 与 豆包 对接 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19494917 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Microsoft-Agent-Framework-%E5%8F%96%E5%87%BA-DeepSeek-%E6%80%9D%E8%80%83%E5%86%85%E5%AE%B9.html&quot;&gt;Microsoft Agent Framework 取出 DeepSeek 思考内容&lt;/a&gt;
&lt;!-- [Microsoft Agent Framework 取出 DeepSeek 思考内容 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19635618 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Microsoft-Agent-Framework-%E6%B5%8B%E8%AF%95%E8%B1%86%E5%8C%85%E7%9A%84%E6%A0%B9%E6%8D%AE%E5%9B%BE%E7%89%87%E7%94%9F%E6%88%90%E7%9F%A2%E9%87%8F%E5%9B%BE%E7%9A%84%E8%83%BD%E5%8A%9B.html&quot;&gt;Microsoft Agent Framework 测试豆包的根据图片生成矢量图的能力&lt;/a&gt;
&lt;!-- [Microsoft Agent Framework 测试豆包的根据图片生成矢量图的能力 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19727344 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%AF%B9%E6%8E%A5%E8%B1%86%E5%8C%85%E6%A8%A1%E5%9E%8B%E6%97%B6%E5%A6%82%E4%BD%95%E6%8E%A7%E5%88%B6%E6%98%AF%E5%90%A6%E8%BF%9B%E5%85%A5%E6%80%9D%E8%80%83%E6%A8%A1%E5%BC%8F.html&quot;&gt;dotnet 对接豆包模型时如何控制是否进入思考模式&lt;/a&gt;
&lt;!-- [dotnet 对接豆包模型时如何控制是否进入思考模式 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19761701 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-Microsoft-Agent-Framework-%E9%85%8D%E7%BD%AE%E8%B0%83%E7%94%A8%E5%B7%A5%E5%85%B7%E5%90%8E%E9%80%80%E5%87%BA%E5%AF%B9%E8%AF%9D.html&quot;&gt;dotnet Microsoft Agent Framework 配置调用工具后退出对话&lt;/a&gt;
&lt;!-- [dotnet Microsoft Agent Framework 配置调用工具后退出对话 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19773412 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E7%94%A8-Microsoft-Agent-Framework-%E5%AF%B9%E6%8E%A5%E8%B1%86%E5%8C%85%E5%A4%A7%E6%A8%A1%E5%9E%8B%E5%AE%9E%E7%8E%B0AI%E8%87%AA%E5%8A%A8%E7%8E%A9-%E8%B0%81%E6%98%AF%E5%8D%A7%E5%BA%95-%E6%B8%B8%E6%88%8F.html&quot;&gt;用 Microsoft Agent Framework 对接豆包大模型实现AI自动玩「谁是卧底」游戏&lt;/a&gt;
&lt;!-- [用 Microsoft Agent Framework 对接豆包大模型实现AI自动玩「谁是卧底」游戏 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19741764 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;semantickernel-1&quot;&gt;SemanticKernel&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-SemanticKernel-%E5%85%A5%E9%97%A8-%E5%BC%80%E7%AF%87.html&quot;&gt;dotnet SemanticKernel 入门 开篇&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-SemanticKernel-%E5%85%A5%E9%97%A8-%E8%B0%83%E7%94%A8%E5%8E%9F%E7%94%9F%E6%9C%AC%E6%9C%BA%E6%8A%80%E8%83%BD.html&quot;&gt;dotnet SemanticKernel 入门 调用原生本机技能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-SemanticKernel-%E5%85%A5%E9%97%A8-%E5%B0%86%E6%8A%80%E8%83%BD%E5%AF%BC%E5%85%A5%E6%A1%86%E6%9E%B6.html&quot;&gt;dotnet SemanticKernel 入门 将技能导入框架&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-SemanticKernel-%E5%85%A5%E9%97%A8-%E6%B3%A8%E5%85%A5%E6%97%A5%E5%BF%97.html&quot;&gt;dotnet SemanticKernel 入门 注入日志&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-SemanticKernel-%E5%85%A5%E9%97%A8-%E8%87%AA%E5%AE%9A%E4%B9%89%E5%8F%98%E9%87%8F%E5%92%8C%E6%8A%80%E8%83%BD.html&quot;&gt;dotnet SemanticKernel 入门 自定义变量和技能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E7%AE%80%E5%8D%95%E6%8E%A7%E5%88%B6%E5%8F%B0%E4%BD%BF%E7%94%A8-KernelMemory-%E5%90%91%E9%87%8F%E5%8C%96%E6%96%87%E6%9C%AC%E5%B5%8C%E5%85%A5%E7%94%9F%E6%88%90%E5%92%8C%E6%9F%A5%E8%AF%A2.html&quot;&gt;dotnet 简单控制台使用 KernelMemory 向量化文本嵌入生成和查询&lt;/a&gt;
&lt;!-- [dotnet 简单控制台使用 KernelMemory 向量化文本嵌入生成和查询 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18250173 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%B0%86%E6%9C%AC%E5%9C%B0%E7%9A%84-Phi-3-%E6%A8%A1%E5%9E%8B%E4%B8%8E-SemanticKernel-%E8%BF%9B%E8%A1%8C%E5%AF%B9%E6%8E%A5.html&quot;&gt;dotnet 将本地的 Phi-3 模型与 SemanticKernel 进行对接&lt;/a&gt;
&lt;!-- [dotnet 将本地的 Phi-3 模型与 SemanticKernel 进行对接 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18257888 ) --&gt;&lt;/p&gt;

&lt;h4 id=&quot;应用&quot;&gt;应用&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/SemanticKernel-%E5%B0%86-LLM-%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%87%BD%E6%95%B0%E5%92%8C%E4%BC%A0%E7%BB%9F%E7%BC%96%E7%A8%8B%E8%9E%8D%E5%90%88%E5%88%B0%E4%B8%80%E8%B5%B7.html&quot;&gt;SemanticKernel 将 LLM 自然语言函数和传统编程融合到一起&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;原理&quot;&gt;原理&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%8E%A2%E7%A9%B6-SemanticKernel-%E7%9A%84-planner-%E7%9A%84%E5%8E%9F%E7%90%86.html&quot;&gt;dotnet 探究 SemanticKernel 的 planner 的原理&lt;/a&gt; &lt;a href=&quot;https://www.cnblogs.com/lindexi/p/17810633.html&quot;&gt;博客园&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;directml&quot;&gt;DirectML&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%9F%BA%E4%BA%8E-DirectML-%E6%8E%A7%E5%88%B6%E5%8F%B0%E8%BF%90%E8%A1%8C-Phi-3-%E6%A8%A1%E5%9E%8B.html&quot;&gt;dotnet 基于 DirectML 控制台运行 Phi-3 模型&lt;/a&gt;
&lt;!-- [dotnet 基于 DirectML 控制台运行 Phi-3 模型 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18245125 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;mcp&quot;&gt;MCP&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-MCP-%E6%97%A0%E9%AD%94%E6%B3%95-%E6%9C%AC%E5%9C%B0%E8%BF%9B%E7%A8%8B%E5%86%85%E6%9C%8D%E5%8A%A1%E7%AB%AF%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%B0%83%E7%94%A8%E5%92%8C%E9%80%9A%E8%AE%AF%E7%A4%BA%E4%BE%8B.html&quot;&gt;dotnet MCP 无魔法 本地进程内服务端客户端调用和通讯示例&lt;/a&gt;
&lt;!-- [dotnet MCP 无魔法 本地进程内服务端客户端调用和通讯示例 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18869179 ) --&gt;&lt;/p&gt;

&lt;h2 id=&quot;aspnet-core&quot;&gt;ASP.NET Core&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/post/asp-dotnet-core-%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA-WebApi-%E6%9C%8D%E5%8A%A1.html&quot;&gt;asp dotnet core 从零开始创建一个 WebApi 服务&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%B8%89%E5%8F%A5%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%88%9B%E5%BB%BA%E8%BF%90%E8%A1%8C%E4%B8%80%E4%B8%AA-web-%E6%9C%8D%E5%8A%A1%E7%A8%8B%E5%BA%8F.html&quot;&gt;dotnet 三句命令行创建运行一个 web 服务程序&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/asp-dotnet-core-%E9%99%90%E5%88%B6%E6%8E%A5%E5%8F%A3%E5%8F%AA%E8%83%BD%E6%9C%AC%E6%9C%BA%E8%AE%BF%E9%97%AE%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;asp dotnet core 限制接口只能本机访问的方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/asp-dotnet-core-%E5%9F%BA%E4%BA%8E-TestServer-%E5%81%9A%E9%9B%86%E6%88%90%E6%B5%8B%E8%AF%95.html&quot;&gt;asp dotnet core 基于 TestServer 做集成测试&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/ASP.NET-Core-%E5%BC%95%E7%94%A8%E5%85%B6%E4%BB%96%E7%A8%8B%E5%BA%8F%E9%9B%86%E9%A1%B9%E7%9B%AE%E9%87%8C%E9%9D%A2%E7%9A%84-Controller-%E6%8E%A7%E5%88%B6%E5%99%A8.html&quot;&gt;ASP.NET Core 引用其他程序集项目里面的 Controller 控制器&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/ASP.NET-Core-%E5%B0%86%E6%96%87%E4%BB%B6%E5%A4%B9%E5%86%85%E5%AE%B9%E8%BE%93%E5%87%BA%E4%B8%BA%E5%8E%8B%E7%BC%A9%E5%8C%85%E6%96%87%E4%BB%B6%E6%96%B9%E6%B3%95.html&quot;&gt;ASP.NET Core 将文件夹内容输出为压缩包文件方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/ASP.NET-Core-%E5%9B%A0%E4%B8%BA-Nginx-%E9%85%8D%E7%BD%AE-Connection-%E4%B8%BA-Upgrade-%E5%AF%BC%E8%87%B4-Kestrel-%E8%BF%94%E5%9B%9E-400-%E9%94%99%E8%AF%AF.html&quot;&gt;ASP.NET Core 因为 Nginx 配置 Connection 为 Upgrade 导致 Kestrel 返回 400 错误&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/ASP.NET-Core-%E8%AD%A6%E6%83%95%E5%8F%AF%E7%A9%BA%E7%B1%BB%E5%9E%8B%E5%BC%80%E5%90%AF%E4%B9%8B%E5%90%8E%E6%A8%A1%E5%9E%8B%E6%A0%A1%E9%AA%8C%E5%A4%B1%E8%B4%A5.html&quot;&gt;ASP.NET Core 警惕可空类型开启之后模型校验失败&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/asp-dotnet-core-3.0-%E6%8E%A5%E5%8F%A3%E8%BF%94%E5%9B%9E-json-%E4%BD%BF%E7%94%A8-PascalCase-%E6%A0%BC%E5%BC%8F.html&quot;&gt;asp dotnet core 3.0 接口返回 json 使用 PascalCase 格式&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/asp-dotnet-core-%E4%B8%8D%E6%AD%A3%E7%BB%8F%E7%9A%84%E6%8F%90%E5%8D%87%E6%95%88%E7%8E%87%E7%9A%84%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95%E6%96%B9%E6%B3%95.html&quot;&gt;asp dotnet core 不正经的提升效率的单元测试方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/asp-dotnet-core-%E8%AE%B0%E4%B8%80%E6%AC%A1%E5%BA%94%E7%94%A8%E6%8B%92%E7%BB%9D%E5%93%8D%E5%BA%94%E8%B0%83%E8%AF%95-%E5%BC%80%E5%90%AF%E7%BA%BF%E7%A8%8B%E7%AD%89%E5%BE%85%E5%90%8C%E6%AD%A5%E7%94%A8%E5%85%89%E7%BA%BF%E7%A8%8B%E6%B1%A0.html&quot;&gt;asp dotnet core 记一次应用拒绝响应调试 开启线程等待同步用光线程池&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/asp-dotnet-core-%E6%8F%90%E4%BE%9B%E5%A4%A7%E6%96%87%E4%BB%B6%E4%B8%8B%E8%BD%BD%E7%9A%84%E6%B5%8B%E8%AF%95.html&quot;&gt;asp dotnet core 提供大文件下载的测试&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[ASP.NET Core 解决控制台输出日志内容前面&lt;a href=&quot;/post/ASP.NET-Core-%E8%A7%A3%E5%86%B3%E6%8E%A7%E5%88%B6%E5%8F%B0%E8%BE%93%E5%87%BA%E6%97%A5%E5%BF%97%E5%86%85%E5%AE%B9%E5%89%8D%E9%9D%A2-40m%E7%AD%89%E4%B9%B1%E7%A0%81%E5%AD%97%E7%AC%A6.html&quot;&gt;40m等乱码字符&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/asp-dotnet-core-%E6%8F%90%E7%A4%BA-Cannot-access-a-disposed-object-%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95.html&quot;&gt;asp dotnet core 提示 Cannot access a disposed object 解决方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%92%8C-ASP.NET-Core-%E9%80%9A%E8%BF%87-elastic-APM-%E4%B8%8A%E6%8A%A5%E4%BF%A1%E6%81%AF.html&quot;&gt;WPF 和 ASP.NET Core 通过 elastic APM 上报信息&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%BD%93-ASP.NET-Core-%E9%93%BE%E6%8E%A5%E6%89%BE%E4%B8%8D%E5%88%B0%E6%97%B6%E5%8F%AF%E8%83%BD%E7%9A%84%E5%8E%9F%E5%9B%A0.html&quot;&gt;当 ASP.NET Core 链接找不到时可能的原因&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/asp-dotnet-core-%E4%BB%8E-Frp-%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E7%9C%9F%E5%AE%9E-IP-%E5%9C%B0%E5%9D%80.html&quot;&gt;asp dotnet core 从 Frp 获取用户真实 IP 地址&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/asp-dotnet-core-%E5%AE%9E%E7%8E%B0%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%8F%91%E9%80%81%E4%BA%8B%E4%BB%B6-Server-Sent-Events-%E7%AE%80%E5%8D%95%E6%96%B9%E5%BC%8F.html&quot;&gt;asp dotnet core 实现服务器发送事件 Server-Sent Events 简单方式&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/asp-dotnet-core-%E9%80%9A%E8%BF%87%E5%9B%BE%E7%89%87%E7%BB%9F%E8%AE%A1-csdn-%E7%94%A8%E6%88%B7%E8%AE%BF%E9%97%AE.html&quot;&gt;asp dotnet core 通过图片统计 csdn 用户访问&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/asp-dotnet-core-%E7%AE%80%E5%8D%95%E5%BC%80%E5%8F%91P2P%E4%B8%AD%E5%A4%AE%E6%9C%8D%E5%8A%A1%E5%99%A8.html&quot;&gt;asp dotnet core 简单开发P2P中央服务器&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/asp-dotnet-core-%E6%94%AF%E6%8C%81%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%B8%8A%E4%BC%A0%E6%96%87%E4%BB%B6.html&quot;&gt;asp dotnet core 支持客户端上传文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/ASP.NET-Core-6-%E6%B5%85%E6%9E%90%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F%E4%B8%8E%E9%85%8D%E7%BD%AE%E7%9A%84%E4%BC%98%E5%85%88%E7%BA%A7.html&quot;&gt;ASP.NET Core 6 浅析环境变量与配置的优先级&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/asp-dotnet-core-%E5%9B%BE%E7%89%87%E5%9C%A8%E6%B5%8F%E8%A7%88%E5%99%A8%E6%B2%A1%E8%AE%BF%E9%97%AE%E5%8F%AF%E8%83%BD%E5%8E%9F%E5%9B%A0.html&quot;&gt;asp dotnet core 图片在浏览器没访问可能原因&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/ASP.NET-Core-%E5%BC%80%E5%90%AF%E5%90%8E%E5%8F%B0%E4%BB%BB%E5%8A%A1.html&quot;&gt;ASP.NET Core 开启后台任务&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/ASP.NET-Core-%E5%86%99%E4%B8%80%E4%B8%AA%E8%83%BD%E5%A4%9F%E6%8E%A5%E6%94%B6%E6%89%80%E6%9C%89%E8%AF%B7%E6%B1%82%E8%B7%AF%E5%BE%84%E7%9A%84%E6%B5%8B%E8%AF%95%E6%9C%8D%E5%8A%A1.html&quot;&gt;ASP.NET Core 写一个能够接收所有请求路径的测试服务&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E7%BB%99MatterMost%E8%AE%A2%E9%98%85RSS%E5%8D%9A%E5%AE%A2.html&quot;&gt;dotnet 给MatterMost订阅RSS博客&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/ASP.NET-Core-%E7%AE%80%E5%8D%95%E7%BB%99-Phi-%E6%A8%A1%E5%9E%8B%E5%B0%81%E8%A3%85%E4%B8%80%E4%B8%AA%E6%9C%8D%E5%8A%A1.html&quot;&gt;ASP.NET Core 简单给 Phi 模型封装一个服务&lt;/a&gt;
&lt;!-- [ASP.NET Core 简单给 Phi 模型封装一个服务 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18592966 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/ASP.NET-Core-%E5%88%B6%E4%BD%9C%E4%B8%80%E4%B8%AA%E4%BD%8E%E8%B5%84%E6%BA%90%E5%8D%A0%E7%94%A8%E7%9A%84%E6%94%AF%E6%8C%81%E8%B6%85%E5%A4%A7%E6%96%87%E4%BB%B6%E8%A1%A8%E5%8D%95%E4%B8%8A%E4%BC%A0%E7%9A%84%E6%9C%8D%E5%8A%A1.html&quot;&gt;ASP.NET Core 制作一个低资源占用的支持超大文件表单上传的服务&lt;/a&gt;
&lt;!-- [ASP.NET Core 制作一个低资源占用的支持超大文件表单上传的服务 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19192031 ) --&gt;&lt;/p&gt;

&lt;h2 id=&quot;blazor&quot;&gt;Blazor&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/post/.NET-Core-%E7%94%A8-Blazor-%E5%81%9A-jmeter-%E7%B3%BB%E5%88%97%E8%A7%86%E9%A2%91.html&quot;&gt;.NET Core 用 Blazor 做 jmeter 系列视频&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-Blazor-%E7%94%A8-C-%E6%8E%A7%E5%88%B6%E7%95%8C%E9%9D%A2%E8%A1%8C%E4%B8%BA.html&quot;&gt;dotnet Blazor 用 C# 控制界面行为&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Blazor-%E8%8E%B7%E5%8F%96%E5%BD%93%E5%89%8D%E7%9A%84-Url-%E9%93%BE%E6%8E%A5.html&quot;&gt;Blazor 获取当前的 Url 链接&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Blazor-%E7%9A%84-NavLink-%E7%9A%84-NavLinkMatch.Prefix-%E6%9C%89%E5%95%A5%E4%BD%9C%E7%94%A8.html&quot;&gt;Blazor 的 NavLink 的 NavLinkMatch.Prefix 有啥作用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Blazor-%E7%9A%84-NavLink-%E6%8F%90%E7%A4%BA-RZ9986-%E4%B8%8D%E6%94%AF%E6%8C%81%E5%A4%8D%E6%9D%82%E5%86%85%E5%AE%B9.html&quot;&gt;Blazor 的 NavLink 提示 RZ9986 不支持复杂内容&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Blazor-%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8%E4%BB%A3%E7%A0%81%E8%B7%B3%E8%BD%AC%E9%93%BE%E6%8E%A5.html&quot;&gt;Blazor 如何使用代码跳转链接&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%B0%86C-%E7%BC%96%E8%AF%91%E4%B8%BAwasm%E8%AE%A9%E5%89%8D%E7%AB%AFhtml%E4%BD%BF%E7%94%A8.html&quot;&gt;dotnet 将C#编译为wasm让前端html使用&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;渲染-1&quot;&gt;渲染&lt;/h2&gt;

&lt;h3 id=&quot;sharpdx&quot;&gt;SharpDx&lt;/h3&gt;

&lt;p&gt;渲染部分，关于 SharpDx 使用，包括入门级教程，请参阅：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-SharpDx-%E6%B8%B2%E6%9F%93%E5%8D%9A%E5%AE%A2%E5%AF%BC%E8%88%AA.html&quot;&gt;WPF 使用 SharpDx 渲染博客导航&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/sharpdx.html&quot;&gt;SharpDX 系列&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;现在 SharpDx 已经不维护了，如果还需要在 C# 使用 Dx 相关技术，可以使用 &lt;a href=&quot;/post/SharpDx-%E7%9A%84%E4%BB%A3%E6%9B%BF%E9%A1%B9%E7%9B%AE.html&quot;&gt;SharpDx 的代替项目&lt;/a&gt; 列出的替代项目&lt;/p&gt;

&lt;h3 id=&quot;vortice&quot;&gt;Vortice&lt;/h3&gt;

&lt;p&gt;由于 SharpDx 已经不维护了，可使用 Vortice 替换，以下是 Vortice 的博客&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/post/DirectX-%E4%BD%BF%E7%94%A8-Vortice-%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E6%8E%A7%E5%88%B6%E5%8F%B0%E5%88%9B%E5%BB%BA-Direct2D1-%E7%AA%97%E5%8F%A3%E4%BF%AE%E6%94%B9%E9%A2%9C%E8%89%B2.html&quot;&gt;DirectX 使用 Vortice 从零开始控制台创建 Direct2D1 窗口修改颜色&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/post/dotnet-DirectX-%E9%80%9A%E8%BF%87-Vortice-%E6%8E%A7%E5%88%B6%E5%8F%B0%E4%BD%BF%E7%94%A8-ID2D1DeviceContext-%E7%BB%98%E5%88%B6%E7%94%BB%E9%9D%A2.html&quot;&gt;dotnet DirectX 通过 Vortice 控制台使用 ID2D1DeviceContext 绘制画面&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E4%BD%BF%E7%94%A8-Vortice-%E6%94%AF%E6%8C%81-Direct2D1-%E7%A6%BB%E5%B1%8F%E6%B8%B2%E6%9F%93.html&quot;&gt;dotnet C# 使用 Vortice 支持 Direct2D1 离屏渲染&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E4%BD%BF%E7%94%A8-Vortice-%E5%88%9B%E5%BB%BA-Direct2D1-%E7%9A%84-ID2D1SolidColorBrush-%E7%BA%AF%E8%89%B2%E7%94%BB%E5%88%B7.html&quot;&gt;dotnet C# 使用 Vortice 创建 Direct2D1 的 ID2D1SolidColorBrush 纯色画刷&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/post/Vortice-%E4%BD%BF%E7%94%A8-DirectComposition-%E6%98%BE%E7%A4%BA%E9%80%8F%E6%98%8E%E7%AA%97%E5%8F%A3.html&quot;&gt;Vortice 使用 DirectComposition 显示透明窗口&lt;/a&gt;
&lt;!-- [Vortice 使用 DirectComposition 显示透明窗口 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19541356 ) --&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/post/dotnet-Vortice-%E6%97%A0%E9%9C%80%E4%BA%A4%E6%8D%A2%E9%93%BE%E4%B8%8E-DirectComposition-%E5%AF%B9%E6%8E%A5%E6%B8%B2%E6%9F%93%E5%B1%82.html&quot;&gt;dotnet Vortice 无需交换链与 DirectComposition 对接渲染层&lt;/a&gt;
&lt;!-- [dotnet Vortice 无需交换链与 DirectComposition 对接渲染层 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19597758 ) --&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-Vortice-%E5%9C%A8-D3DImage-%E6%98%BE%E7%A4%BA-D2D-%E5%86%85%E5%AE%B9.html&quot;&gt;WPF 使用 Vortice 在 D3DImage 显示 D2D 内容&lt;/a&gt;
&lt;!-- [WPF 使用 Vortice 在 D3DImage 显示 D2D 内容 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19541357 ) --&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%AF%B9%E6%8E%A5-Vortice-%E8%B0%83%E7%94%A8-D2D-%E4%BD%BF%E7%94%A8-IWICBitmap-%E7%A6%BB%E5%B1%8F%E6%B8%B2%E6%9F%93.html&quot;&gt;WPF 对接 Vortice 调用 D2D 使用 IWICBitmap 离屏渲染&lt;/a&gt; &lt;a href=&quot;https://www.cnblogs.com/lindexi/p/16774416.html&quot;&gt;博客园&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%AF%B9%E6%8E%A5-Vortice-%E8%B0%83%E7%94%A8-WIC-%E5%8A%A0%E8%BD%BD%E5%9B%BE%E7%89%87.html&quot;&gt;WPF 对接 Vortice 调用 WIC 加载图片&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%AF%B9%E6%8E%A5-Vortice-%E5%9C%A8-Direct2D-%E7%BB%98%E5%88%B6%E4%BB%8E-WIC-%E5%8A%A0%E8%BD%BD%E7%9A%84%E5%9B%BE%E7%89%87.html&quot;&gt;WPF 对接 Vortice 在 Direct2D 绘制从 WIC 加载的图片&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E9%80%9A%E8%BF%87-Vortice-%E4%BD%BF%E7%94%A8-Direct2D-%E7%89%B9%E6%95%88%E5%85%A5%E9%97%A8.html&quot;&gt;dotnet C# 通过 Vortice 使用 Direct2D 特效入门&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E9%80%9A%E8%BF%87-Vortice-%E4%BD%BF%E7%94%A8-Direct2D-%E7%9A%84-ID2D1CommandList-%E5%85%A5%E9%97%A8.html&quot;&gt;dotnet C# 通过 Vortice 使用 Direct2D 的 ID2D1CommandList 入门&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E9%80%9A%E8%BF%87-Vortice-%E5%B0%86-ID2D1CommandList-%E4%BD%9C%E4%B8%BA%E7%89%B9%E6%95%88%E7%9A%84%E8%BE%93%E5%85%A5%E6%BA%90.html&quot;&gt;dotnet C# 通过 Vortice 将 ID2D1CommandList 作为特效的输入源&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/post/%E5%88%86%E4%BA%AB%E4%B8%80%E4%B8%AA%E5%9C%A8-dotnet-%E9%87%8C%E4%BD%BF%E7%94%A8-D2D-%E9%85%8D%E5%90%88-AOT-%E5%BC%80%E5%8F%91%E5%B0%8F%E8%80%8C%E7%BE%8E%E7%9A%84%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91%E7%BB%8F%E9%AA%8C.html&quot;&gt;分享一个在 dotnet 里使用 D2D 配合 AOT 开发小而美的应用开发经验&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/post/dotnet-DirectX-%E5%81%9A%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%BB%98%E5%88%B6%E6%8A%98%E7%BA%BF%E7%AC%94%E8%BF%B9%E7%9A%84-D2D-%E5%BA%94%E7%94%A8.html&quot;&gt;dotnet DirectX 做一个简单绘制折线笔迹的 D2D 应用&lt;/a&gt;
&lt;!-- [dotnet DirectX 做一个简单绘制折线笔迹的 D2D 应用 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18468855 ) --&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/post/dotnet-DirectX-%E9%80%9A%E8%BF%87%E5%8F%AF%E7%AD%89%E5%BE%85%E4%BA%A4%E6%8D%A2%E9%93%BE%E9%99%8D%E4%BD%8E%E8%BE%93%E5%85%A5%E6%B8%B2%E6%9F%93%E5%BB%B6%E8%BF%9F.html&quot;&gt;dotnet DirectX 通过可等待交换链降低输入渲染延迟&lt;/a&gt;
&lt;!-- [dotnet DirectX 通过可等待交换链降低输入渲染延迟 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19643684 ) --&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/post/dotnet-Vortice-%E9%80%9A%E8%BF%87-Angle-%E5%B0%86-Skia-%E5%92%8C-DirectX-%E5%AF%B9%E6%8E%A5.html&quot;&gt;dotnet Vortice 通过 Angle 将 Skia 和 DirectX 对接&lt;/a&gt;
&lt;!-- [dotnet Vortice 通过 Angle 将 Skia 和 DirectX 对接 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19593476 ) --&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;更多关于 DirectX 相关，请参阅 DirectX 官方博客： &lt;a href=&quot;https://devblogs.microsoft.com/directx/landing-page/&quot;&gt;DirectX Landing Page - DirectX Developer Blog&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;skia&quot;&gt;Skia&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BF%AE%E5%A4%8D%E5%9C%A8-Linux-%E4%B8%8A%E4%BD%BF%E7%94%A8-SkiaSharp-%E6%8F%90%E7%A4%BA%E6%89%BE%E4%B8%8D%E5%88%B0-liblibSkiaSharp-%E5%BA%93.html&quot;&gt;dotnet 修复在 Linux 上使用 SkiaSharp 提示找不到 liblibSkiaSharp 库&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%A7%A3%E5%86%B3-Skia-%E5%9B%A0%E4%B8%BA%E6%89%BE%E4%B8%8D%E5%88%B0%E5%AD%97%E4%BD%93%E8%80%8C%E7%BB%98%E5%88%B6%E4%B8%8D%E5%87%BA%E4%B8%AD%E6%96%87%E5%AD%97%E7%AC%A6.html&quot;&gt;WPF 解决 Skia 因为找不到字体而绘制不出中文字符&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%8E%A2%E7%B4%A2-Skia-%E7%9A%84%E7%AB%96%E6%8E%92%E6%96%87%E6%9C%AC%E6%B8%B2%E6%9F%93%E7%9A%84%E5%AD%97%E7%AC%A6%E9%AB%98%E5%BA%A6.html&quot;&gt;WPF 探索 Skia 的竖排文本渲染的字符高度&lt;/a&gt;
&lt;!-- [WPF 探索 Skia 的竖排文本渲染的字符高度 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18815810 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-Skia-%E7%BB%98%E5%88%B6-WriteableBitmap-%E5%9B%BE%E7%89%87.html&quot;&gt;WPF 使用 Skia 绘制 WriteableBitmap 图片&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E8%87%AA%E5%B7%B1%E5%B0%81%E8%A3%85-Skia-%E5%B7%AE%E9%87%8F%E7%BB%98%E5%88%B6%E6%8E%A7%E4%BB%B6.html&quot;&gt;WPF 自己封装 Skia 差量绘制控件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-Skia-%E8%A7%A3%E6%9E%90%E7%BB%98%E5%88%B6-SVG-%E5%9B%BE%E7%89%87.html&quot;&gt;WPF 使用 Skia 解析绘制 SVG 图片&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%8E%A7%E5%88%B6%E5%8F%B0-%E4%BD%BF%E7%94%A8-Microsoft.Maui.Graphics-%E9%85%8D%E5%90%88-Skia-%E8%BF%9B%E8%A1%8C%E7%BB%98%E5%9B%BE%E5%85%A5%E9%97%A8.html&quot;&gt;dotnet 控制台 使用 Microsoft.Maui.Graphics 配合 Skia 进行绘图入门&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Microsoft.Maui.Graphics.Skia-%E4%BD%BF%E7%94%A8-DrawString-%E7%BB%98%E5%88%B6%E6%96%87%E6%9C%AC%E7%9A%84%E5%9D%90%E6%A0%87%E9%97%AE%E9%A2%98.html&quot;&gt;Microsoft.Maui.Graphics.Skia 使用 DrawString 绘制文本的坐标问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/SkiaSharp-%E6%B8%B2%E6%9F%93%E8%BE%93%E5%87%BA-SVG-%E6%96%87%E4%BB%B6.html&quot;&gt;SkiaSharp 渲染输出 SVG 文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/sunnytrudeau/p/15515080.html&quot;&gt;SkiaSharp跨平台绘图研究1-WPF桌面应用 - SunnyTrudeau - 博客园&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/sunnytrudeau/p/15515282.html&quot;&gt;SkiaSharp跨平台绘图研究2-Xamarin.Forms移动应用 - SunnyTrudeau - 博客园&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/sunnytrudeau/p/15563443.html&quot;&gt;SkiaSharp跨平台绘图研究3-Asp.Net Core网站 - SunnyTrudeau - 博客园&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/sunnytrudeau/p/15570085.html&quot;&gt;SkiaSharp跨平台绘图研究4-在PDF上绘图 - SunnyTrudeau - 博客园&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/sunnytrudeau/p/15574467.html&quot;&gt;SkiaSharp跨平台绘图研究5-Blazor WebAssembly网页绘图 - SunnyTrudeau - 博客园&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/yycelsu/p/14048859.html&quot;&gt;Docker环境下使用SkiaSharp的2种方式 - 从零开始-DotNET技术 - 博客园&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E7%AE%80%E5%8D%95%E8%81%8A%E8%81%8A-Skia-%E9%87%8C%E7%9A%84-SKFontMetrics-%E7%9A%84%E5%90%84%E9%A1%B9%E5%B1%9E%E6%80%A7%E4%BD%9C%E7%94%A8.html&quot;&gt;dotnet 简单聊聊 Skia 里的 SKFontMetrics 的各项属性作用&lt;/a&gt;
&lt;!-- [dotnet 简单聊聊 Skia 里的 SKFontMetrics 的各项属性作用 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18621674 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/SkiaSharp-%E4%BD%BF%E7%94%A8-HarfBuzz-%E4%BF%AE%E5%A4%8D%E6%89%BE%E4%B8%8D%E5%88%B0-Symbol-%E5%AD%97%E5%BD%A2.html&quot;&gt;SkiaSharp 使用 HarfBuzz 修复找不到 Symbol 字形&lt;/a&gt;
&lt;!-- [SkiaSharp 使用 HarfBuzz 修复找不到 Symbol 字形 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19014430 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Skia-%E5%9C%A8%E9%BE%99%E8%8A%AF%E6%90%AD%E6%99%AF%E5%98%89%E5%BE%AE%E6%98%BE%E5%8D%A1%E8%AE%BE%E5%A4%87-%E6%9F%90%E4%BA%9B%E5%AD%97%E4%BD%93%E4%BC%9A%E6%B8%B2%E6%9F%93%E7%9B%B8%E4%BA%92%E8%A6%86%E7%9B%96.html&quot;&gt;Skia 在龙芯搭景嘉微显卡设备 某些字体会渲染相互覆盖&lt;/a&gt;
&lt;!-- [Skia 在龙芯搭景嘉微显卡设备 某些字体会渲染相互覆盖 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19212117 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;wpf-渲染相关&quot;&gt;WPF 渲染相关&lt;/h3&gt;

&lt;p&gt;我写了一些关于 WPF 底层渲染和渲染定制以及 WPF 与其他渲染库对接的博客，请参阅： &lt;a href=&quot;https://blog.csdn.net/lindexi_gd/category_9276313.html?spm=1001.2014.3001.5482&quot;&gt;WPF 底层渲染_lindexi_gd的博客-CSDN博客&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%B8%B2%E6%9F%93%E5%8E%9F%E7%90%86.html&quot;&gt;WPF 渲染原理&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-SharpDx-%E6%B8%B2%E6%9F%93%E5%8D%9A%E5%AE%A2%E5%AF%BC%E8%88%AA.html&quot;&gt;WPF 使用 SharpDx 渲染博客导航&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BB%8E-DX-%E5%B1%82%E9%9D%A2%E8%AE%B2-WPF-%E6%B8%B2%E6%9F%93%E5%8D%A1%E9%A1%BF.html&quot;&gt;从 DX 层面讲 WPF 渲染卡顿&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Windows-%E6%A0%B8%E5%BF%83%E7%BC%96%E7%A8%8B%E7%AC%94%E8%AE%B0-Direct2D-%E6%AF%94-GDI-%E5%BF%AB%E7%9A%84%E4%B8%80%E9%83%A8%E5%88%86.html&quot;&gt;Windows 核心编程笔记 Direct2D 比 GDI 快的一部分&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-SharpDX-%E5%9C%A8-D3DImage-%E6%98%BE%E7%A4%BA.html&quot;&gt;WPF 使用 SharpDX 在 D3DImage 显示&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;silknet&quot;&gt;Silk.NET&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-Silk.NET-%E8%BF%9B%E8%A1%8C-DirectX-%E6%B8%B2%E6%9F%93%E5%85%A5%E9%97%A8.html&quot;&gt;WPF 使用 Silk.NET 进行 DirectX 渲染入门&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-Silk.NET-%E8%BF%9B%E8%A1%8C-Direct2D-%E6%B8%B2%E6%9F%93%E5%85%A5%E9%97%A8.html&quot;&gt;WPF 使用 Silk.NET 进行 Direct2D 渲染入门&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BD%BF%E7%94%A8-Silk.NET-%E5%88%9B%E5%BB%BA-OpenGL-%E7%A9%BA%E7%AA%97%E5%8F%A3%E9%A1%B9%E7%9B%AE%E4%BE%8B%E5%AD%90.html&quot;&gt;使用 Silk.NET 创建 OpenGL 空窗口项目例子&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BD%BF%E7%94%A8-Silk.NET-%E8%B0%83%E7%94%A8-DirectWrite-%E8%8E%B7%E5%8F%96%E5%AD%97%E4%BD%93-Font-Metrics-%E4%BF%A1%E6%81%AF.html&quot;&gt;使用 Silk.NET 调用 DirectWrite 获取字体 Font Metrics 信息&lt;/a&gt;
&lt;!-- [使用 Silk.NET 调用 DirectWrite 获取字体 Font Metrics 信息 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19001918 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;opentk-1&quot;&gt;OpenTK&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/OpenTK-%E5%85%A5%E9%97%A8-%E5%88%9D%E5%A7%8B%E5%8C%96%E7%AA%97%E5%8F%A3.html&quot;&gt;OpenTK 入门 初始化窗口&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/OpenTK-%E5%85%A5%E9%97%A8-Vsync-%E5%9E%82%E7%9B%B4%E5%90%8C%E6%AD%A5%E5%AF%B9%E5%88%B7%E6%96%B0%E7%8E%87%E7%9A%84%E5%BD%B1%E5%93%8D.html&quot;&gt;OpenTK 入门 Vsync 垂直同步对刷新率的影响&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;directwrite&quot;&gt;DirectWrite&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BD%BF%E7%94%A8-Silk.NET-%E8%B0%83%E7%94%A8-DirectWrite-%E8%8E%B7%E5%8F%96%E5%AD%97%E4%BD%93-Font-Metrics-%E4%BF%A1%E6%81%AF.html&quot;&gt;使用 Silk.NET 调用 DirectWrite 获取字体 Font Metrics 信息&lt;/a&gt;
&lt;!-- [使用 Silk.NET 调用 DirectWrite 获取字体 Font Metrics 信息 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19001918 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/DirectWrite-%E9%80%9A%E8%BF%87-GetUnicodeRanges-%E8%8E%B7%E5%8F%96%E5%AD%97%E4%BD%93%E8%83%BD%E6%94%AF%E6%8C%81%E7%9A%84%E5%AD%97%E7%AC%A6%E8%8C%83%E5%9B%B4.html&quot;&gt;DirectWrite 通过 GetUnicodeRanges 获取字体能支持的字符范围&lt;/a&gt;
&lt;!-- [DirectWrite 通过 GetUnicodeRanges 获取字体能支持的字符范围 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19360019 ) --&gt;&lt;/p&gt;

&lt;h2 id=&quot;openxml&quot;&gt;OpenXML&lt;/h2&gt;

&lt;p&gt;使用 OpenXML 可以对 Office 文档进行底层的处理，相关博客比较多，请参阅：&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Office-%E4%BD%BF%E7%94%A8-OpenXML-SDK-%E8%A7%A3%E6%9E%90%E6%96%87%E6%A1%A3%E5%8D%9A%E5%AE%A2%E7%9B%AE%E5%BD%95.html&quot;&gt;Office 使用 OpenXML SDK 解析文档博客目录&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;调试-1&quot;&gt;调试&lt;/h2&gt;

&lt;p&gt;代码的调试方法，常用的套路和经验&lt;/p&gt;

&lt;p&gt;入门的大博客： &lt;a href=&quot;/post/dotnet-%E4%BB%A3%E7%A0%81%E8%B0%83%E8%AF%95%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet 代码调试方法&lt;/a&gt;
&lt;!-- [dotnet 代码调试方法-腾讯云开发者社区-腾讯云](https://cloud.tencent.com/developer/article/1446477 ) --&gt;
&lt;!-- [dotnet 代码调试方法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18960345 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Windows-%E8%B0%83%E8%AF%95%E5%B7%A5%E5%85%B7%E8%AF%BE%E7%A8%8B.html&quot;&gt;Windows 调试工具课程&lt;/a&gt;
&lt;!-- [Windows 调试工具课程 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18421353 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%B0%83%E8%AF%95%E5%BA%94%E7%94%A8%E5%90%AF%E5%8A%A8%E9%97%AA%E9%80%80%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet 调试应用启动闪退的方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AE%B0%E5%9B%A0%E4%B8%BA-NVIDIA-%E6%98%BE%E9%A9%B1%E9%94%99%E8%AF%AF%E8%80%8C%E8%AE%A9-WPF-%E5%BA%94%E7%94%A8%E5%90%AF%E5%8A%A8%E9%97%AA%E9%80%80%E9%97%AE%E9%A2%98.html&quot;&gt;记因为 NVIDIA 显驱错误而让 WPF 应用启动闪退问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AE%B0%E4%B8%80%E6%AC%A1%E8%B0%83%E8%AF%95%E8%B5%84%E6%BA%90%E7%AE%A1%E7%90%86%E5%99%A8%E6%9C%AA%E5%93%8D%E5%BA%94%E7%BB%8F%E9%AA%8C.html&quot;&gt;记一次调试资源管理器未响应经验&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AE%B0%E8%B0%83%E8%AF%95-RX-Explorer-WAS-%E6%96%87%E4%BB%B6%E7%AE%A1%E7%90%86%E5%99%A8-UI-%E6%9C%AA%E5%93%8D%E5%BA%94%E9%97%AE%E9%A2%98.html&quot;&gt;记调试 RX-Explorer-WAS 文件管理器 UI 未响应问题&lt;/a&gt;
&lt;!-- [记调试 RX-Explorer-WAS 文件管理器 UI 未响应问题 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19606611 ) --&gt;
&lt;!-- [记调试 RX-Explorer-WAS 文件管理器 UI 未响应问题-腾讯云开发者社区-腾讯云](https://cloud.tencent.com/developer/article/2629642 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E8%B0%83%E8%AF%95%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F%E6%96%B9%E6%B3%95.html&quot;&gt;VisualStudio 调试内存泄漏方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E5%BC%80%E5%90%AF%E4%BB%85%E6%88%91%E4%BB%A3%E7%A0%81%E8%B0%83%E8%AF%95.html&quot;&gt;VisualStudio 开启仅我代码调试&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WinDbg-%E5%8A%A0%E8%BD%BD-dotnet-core-%E7%9A%84-sos.dll-%E8%BE%85%E5%8A%A9%E8%B0%83%E8%AF%95%E6%96%B9%E6%B3%95.html&quot;&gt;WinDbg 加载 dotnet core 的 sos.dll 辅助调试方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-windbg-%E8%BF%90%E8%A1%8C%E8%84%9A%E6%9C%AC%E6%96%B9%E5%BC%8F%E8%87%AA%E5%8A%A8%E6%89%B9%E9%87%8F%E8%B0%83%E8%AF%95%E5%A4%84%E7%90%86-dump-%E6%96%87%E4%BB%B6.html&quot;&gt;dotnet 使用 windbg 运行脚本方式自动批量调试处理 dump 文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WinDbg-%E8%AE%BE%E7%BD%AE%E5%9C%A8%E5%8A%A0%E8%BD%BD%E5%88%B0%E6%9F%90%E4%B8%AA-DLL-%E8%BF%9B%E5%85%A5%E6%96%AD%E7%82%B9.html&quot;&gt;WinDbg 设置在加载到某个 DLL 进入断点&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BC%98%E9%9B%85%E8%B0%83%E8%AF%95-REST-API-%E7%9A%84%E5%B7%A5%E5%85%B7.html&quot;&gt;优雅调试 REST API 的工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%B0%83%E8%AF%95%E6%97%B6%E9%99%90%E5%88%B6%E7%A8%8B%E5%BA%8F%E4%BD%BF%E7%94%A8-CPU-%E6%A0%B8%E5%BF%83%E6%95%B0%E6%A8%A1%E6%8B%9F%E4%BD%8E%E7%AB%AF%E8%AE%BE%E5%A4%87.html&quot;&gt;调试时限制程序使用 CPU 核心数模拟低端设备&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;p&gt;&lt;a href=&quot;/post/%E5%9C%A8-Windows-%E4%B8%8B%E9%82%A3%E4%BA%9B%E5%A5%BD%E7%94%A8%E7%9A%84%E8%B0%83%E8%AF%95%E8%BD%AF%E4%BB%B6.html&quot;&gt;在 Windows 下那些好用的调试软件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%B0%83%E8%AF%95-ms-%E6%BA%90%E4%BB%A3%E7%A0%81.html&quot;&gt;调试 ms 源代码&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%A6%82%E4%BD%95%E8%B0%83%E8%AF%95%E6%9F%90%E4%B8%AA%E6%96%87%E4%BB%B6%E6%98%AF%E5%93%AA%E4%B8%AA%E4%BB%A3%E7%A0%81%E5%88%9B%E5%BB%BA.html&quot;&gt;dotnet 如何调试某个文件是哪个代码创建&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-core-%E8%BE%93%E5%87%BA%E8%B0%83%E8%AF%95%E4%BF%A1%E6%81%AF%E5%88%B0-DebugView-%E8%BD%AF%E4%BB%B6.html&quot;&gt;dotnet core 输出调试信息到 DebugView 软件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E4%BD%BF%E7%94%A8-FastTunnel-%E8%BE%85%E5%8A%A9%E6%90%AD%E5%BB%BA%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%95%E7%8E%AF%E5%A2%83.html&quot;&gt;VisualStudio 使用 FastTunnel 辅助搭建远程调试环境&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AE%B0%E5%86%85%E5%AD%98%E6%9D%A1%E7%A1%AC%E4%BB%B6%E6%8D%9F%E5%9D%8F%E8%93%9D%E5%B1%8F%E7%9A%84-dump-%E6%96%87%E4%BB%B6%E5%88%86%E6%9E%90.html&quot;&gt;记内存条硬件损坏蓝屏的 dump 文件分析&lt;/a&gt;
&lt;!-- [记内存条硬件损坏蓝屏的 dump 文件分析 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18293074 ) --&gt;&lt;/p&gt;

&lt;h2 id=&quot;windows&quot;&gt;Windows&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/post/Windows-%E9%80%9A%E8%BF%87%E7%BC%96%E8%BE%91%E6%B3%A8%E5%86%8C%E8%A1%A8%E8%AE%BE%E7%BD%AE%E5%B7%A6%E5%8F%B3%E6%89%8B%E4%BD%BF%E7%94%A8%E4%B9%A0%E6%83%AF%E6%9B%B4%E6%94%B9-Popup-%E5%BC%B9%E5%87%BA%E4%BD%8D%E7%BD%AE.html&quot;&gt;Windows 通过编辑注册表设置左右手使用习惯更改 Popup 弹出位置&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-%E5%AE%89%E8%A3%85Mpi.html&quot;&gt;win10 安装Mpi&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%BD%B1%E5%AD%90%E7%B3%BB%E7%BB%9F%E8%AE%A9-C++-%E7%A8%8B%E5%BA%8F%E6%97%A0%E6%B3%95%E8%BF%90%E8%A1%8C.html&quot;&gt;影子系统让 C++ 程序无法运行&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%AE%89%E8%A3%85-Sureface-Hub-%E7%B3%BB%E7%BB%9F-Windows-10-team-PPIPro-%E7%B3%BB%E7%BB%9F.html&quot;&gt;安装 Sureface Hub 系统 Windows 10 team PPIPro 系统&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%A6%82%E4%BD%95%E5%9C%A8-Windows-10-%E4%B8%AD%E7%A7%BB%E9%99%A4-Internet-Explorer-%E6%B5%8F%E8%A7%88%E5%99%A8.html&quot;&gt;如何在 Windows 10 中移除 Internet Explorer 浏览器&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E6%81%A2%E5%A4%8D-U-%E7%9B%98%E9%9A%90%E8%97%8F%E6%96%87%E4%BB%B6%E5%A4%B9.html&quot;&gt;恢复 U 盘隐藏文件夹&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%9B%BD%E5%86%85%E5%A5%BD%E7%94%A8%E7%9A%84-DNS-%E5%88%97%E8%A1%A8.html&quot;&gt;国内好用的 DNS 列表&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/windows-10%E9%A2%84%E8%A7%88%E7%89%88%E5%8D%87%E7%BA%A7win10-7%E6%9C%8829-10240.16384.html&quot;&gt;windows 10预览版升级win10 7月29 10240.16384&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/windows-10-%E8%AE%BE%E7%BD%AE-%E5%BA%94%E7%94%A8%E5%AE%8C%E6%95%B4ms-settings%E5%BF%AB%E6%8D%B7%E6%96%B9%E5%BC%8F%E6%B1%87%E6%80%BB.md.html&quot;&gt;windows-10「设置」应用完整ms-settings快捷方式汇总.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-%E6%9C%AC%E5%9C%B0%E9%80%82%E9%85%8D%E5%99%A8%E4%B8%8D%E6%94%AF%E6%8C%81%E9%87%8D%E8%A6%81%E7%9A%84%E4%BD%8E%E8%83%BD%E8%80%97%E6%8E%A7%E5%88%B6%E5%99%A8%E7%8A%B6%E6%80%81.html&quot;&gt;win10 本地适配器不支持重要的低能耗控制器状态&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-%E4%BD%BF%E7%94%A8-SMB-v1.html&quot;&gt;win10 使用 SMB v1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/windows-%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E5%9C%A8%E5%85%B3%E6%9C%BA%E6%97%B6%E7%9A%84%E9%80%80%E5%87%BA%E4%BB%A3%E5%8F%B7.html&quot;&gt;windows 应用程序在关机时的退出代号&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win7-%E5%8D%87%E7%BA%A7%E5%88%B0-win10-%E8%A1%A5%E4%B8%81.html&quot;&gt;win7 升级到 win10 补丁&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Windows-10-16251-%E6%B7%BB%E5%8A%A0%E7%9A%84-api.html&quot;&gt;Windows 10 16251 添加的 api&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%A7%A3%E5%86%B3-Win10-UWP-%E6%97%A0%E6%B3%95%E4%BD%BF%E7%94%A8-ss-%E8%BF%9E%E6%8E%A5.html&quot;&gt;解决 Win10 UWP 无法使用 ss 连接&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%BE%AE%E8%BD%AF%E5%90%84%E7%89%88%E6%9C%AC%E7%B3%BB%E7%BB%9F%E4%B8%8D%E5%86%8D%E7%BB%B4%E6%8A%A4%E6%97%B6%E9%97%B4.html&quot;&gt;微软各版本系统不再维护时间&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%A6%82%E4%BD%95%E8%AE%A9-USB-%E8%AE%BE%E5%A4%87%E4%B8%8D%E6%98%BE%E7%A4%BA%E5%AE%89%E5%85%A8%E5%88%A0%E9%99%A4%E7%A1%AC%E4%BB%B6%E5%BC%B9%E5%87%BA%E9%80%89%E9%A1%B9.html&quot;&gt;如何让 USB 设备不显示安全删除硬件弹出选项&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E6%89%8B%E6%9C%BA1520-win8.1%E5%8D%87%E7%BA%A7win10.html&quot;&gt;手机1520 win8.1升级win10&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Windows-%E5%AF%B9%E5%85%A8%E5%B1%8F%E5%BA%94%E7%94%A8%E7%9A%84%E4%BC%98%E5%8C%96.html&quot;&gt;Windows 对全屏应用的优化&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BD%BF%E7%94%A8-DISM-%E5%AE%89%E5%85%A8%E6%B8%85%E7%90%86-C-%E7%9B%98-WinSxS-%E6%96%87%E4%BB%B6%E5%A4%B9%E7%A9%BA%E9%97%B4.html&quot;&gt;使用 DISM 安全清理 C 盘 WinSxS 文件夹空间&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.ithome.com/0/563/541.htm&quot;&gt;Win10 Win11 如何真正获取 Trustedinstaller 权限（非修改所有者及权限） - IT之家&lt;/a&gt;
&lt;!-- 
Import-Module NtObjectManager
sc.exe start TrustedInstaller
Set-NtTokenPrivilege SeDebugPrivilege 
$p = Get-NtProcess -Name TrustedInstaller.exe
$proc = New-Win32Process cmd.exe -CreationFlags NewConsole -ParentProcess $p
 --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://learn.microsoft.com/zh-cn/windows/win32/fileio/filesystem-functionality-comparison&quot;&gt;文件系统功能比较 NTFS、exFAT、UDF 和 FAT32 的功能和功能支持比较 - Win32 apps - Microsoft Learn&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/PowerShell-%E8%AE%BE%E7%BD%AE-Windows-%E5%AE%89%E5%85%A8%E4%B8%AD%E5%BF%83%E7%99%BD%E5%90%8D%E5%8D%95%E6%8E%92%E9%99%A4%E9%A1%B9.html&quot;&gt;PowerShell 设置 Windows 安全中心白名单排除项&lt;/a&gt;
&lt;!-- [PowerShell 设置 Windows 安全中心白名单排除项 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19424072 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;windows-编程&quot;&gt;Windows 编程&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-17025-%E8%A7%A6%E6%91%B8bug.html&quot;&gt;win10 17025 触摸bug&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/d3dcompiler_47.dll-%E6%97%A0%E6%B3%95%E5%AE%9A%E4%BD%8D%E7%A8%8B%E5%BA%8F%E8%BE%93%E5%85%A5%E7%82%B9__CxxFrameHandler4%E4%BA%8E%E5%8A%A8%E6%80%81%E9%93%BE%E6%8E%A5%E5%BA%93.html&quot;&gt;d3dcompiler_47.dll 无法定位程序输入点__CxxFrameHandler4于动态链接库&lt;/a&gt;
&lt;!-- [d3dcompiler_47.dll 无法定位程序输入点__CxxFrameHandler4于动态链接库 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18845817 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Windows-%E7%AA%97%E5%8F%A3%E6%A0%B7%E5%BC%8F-%E4%BB%80%E4%B9%88%E6%98%AF-WS_EX_NOREDIRECTIONBITMAP-%E6%A0%B7%E5%BC%8F.html&quot;&gt;Windows 窗口样式 什么是 WS_EX_NOREDIRECTIONBITMAP 样式&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E7%94%A8-SetWindowPos-%E6%96%B9%E6%B3%95%E8%AE%BE%E7%BD%AE%E4%B8%80%E4%B8%AA%E5%81%9C%E6%AD%A2%E5%93%8D%E5%BA%94%E7%9A%84%E7%AA%97%E5%8F%A3%E5%B0%86%E5%8D%A1%E8%B0%83%E7%94%A8%E6%96%B9.html&quot;&gt;用 SetWindowPos 方法设置一个停止响应的窗口将卡调用方&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Windows-%E7%9A%84-Pen-%E5%8D%8F%E8%AE%AE.html&quot;&gt;Windows 的 Pen 协议&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/XP-%E6%BA%90%E4%BB%A3%E7%A0%81-%E5%A6%82%E4%BD%95%E5%9C%A8%E6%B8%85%E7%A9%BA%E5%9B%9E%E6%94%B6%E7%AB%99%E6%97%B6%E4%BF%AE%E6%94%B9%E5%9B%9E%E6%94%B6%E7%AB%99%E5%9B%BE%E6%A0%87.html&quot;&gt;XP 源代码 如何在清空回收站时修改回收站图标&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Windows-%E6%A0%B8%E5%BF%83%E7%BC%96%E7%A8%8B%E7%AC%94%E8%AE%B0-Direct2D-%E6%AF%94-GDI-%E5%BF%AB%E7%9A%84%E4%B8%80%E9%83%A8%E5%88%86.html&quot;&gt;Windows 核心编程笔记 Direct2D 比 GDI 快的一部分&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%A6%82%E4%BD%95%E8%AE%A9%E8%BD%AF%E4%BB%B6%E4%BB%A5-System-%E6%9D%83%E9%99%90%E8%BF%90%E8%A1%8C.html&quot;&gt;如何让软件以 System 权限运行&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%88%A0%E9%99%A4%E8%87%AA%E8%BA%AB%E7%A8%8B%E5%BA%8F%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet 删除自身程序的方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E9%80%9A%E8%BF%87%E7%BC%96%E7%A8%8B%E7%9A%84%E6%96%B9%E6%B3%95%E5%9C%A8%E6%A1%8C%E9%9D%A2%E5%88%9B%E5%BB%BA%E5%9B%9E%E6%94%B6%E7%AB%99%E5%BF%AB%E6%8D%B7%E6%96%B9%E5%BC%8F.html&quot;&gt;C# 通过编程的方法在桌面创建回收站快捷方式&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-core-%E5%AF%BC%E5%87%BA-COM-%E7%BB%84%E4%BB%B6.html&quot;&gt;dotnet core 导出 COM 组件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E5%88%A4%E6%96%AD%E7%B3%BB%E7%BB%9F%E7%89%88%E6%9C%AC.html&quot;&gt;C# 判断系统版本&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Win32-%E4%BD%BF%E7%94%A8-CreateProcess-%E6%96%B9%E6%B3%95%E8%AE%A9%E4%BB%BB%E5%8A%A1%E7%AE%A1%E7%90%86%E5%99%A8%E9%87%8C%E7%9A%84%E5%91%BD%E4%BB%A4%E8%A1%8C%E4%B8%8D%E6%98%BE%E7%A4%BA%E5%BA%94%E7%94%A8%E6%96%87%E4%BB%B6%E8%B7%AF%E5%BE%84.html&quot;&gt;Win32 使用 CreateProcess 方法让任务管理器里的命令行不显示应用文件路径&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%A6%82%E4%BD%95%E6%9B%BF%E6%8D%A2%E4%B8%80%E4%B8%AA-exe-%E7%9A%84%E5%9B%BE%E6%A0%87.html&quot;&gt;如何替换一个 exe 的图标&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win-%E6%B6%88%E6%81%AF.html&quot;&gt;win 消息&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%89%AA%E8%B4%B4%E6%9D%BF%E8%A2%AB%E5%8D%A0%E7%94%A8%E5%AF%BC%E8%87%B4%E5%BA%94%E7%94%A8%E4%BD%BF%E7%94%A8%E5%89%AA%E8%B4%B4%E6%9D%BF%E6%8B%B7%E8%B4%9D%E5%86%85%E5%AE%B9%E5%A4%B1%E8%B4%A5%E6%8A%9B%E5%87%BA-COMException-0x800401D0-%E9%94%99%E8%AF%AF.html&quot;&gt;剪贴板被占用导致应用使用剪贴板拷贝内容失败抛出 COMException 0x800401D0 错误&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/kybs0/p/17985128&quot;&gt;C# 息屏操作出现闪屏 - 唐宋元明清2188 - 博客园&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E4%BD%BF%E7%94%A8-Win32-%E5%87%BD%E6%95%B0%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E4%B8%8B%E8%BD%BD%E6%96%87%E4%BB%B6%E5%A4%B9%E7%9A%84%E8%B7%AF%E5%BE%84%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet C# 使用 Win32 函数获取用户下载文件夹的路径的方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win32-%E4%BD%8E%E5%86%85%E5%AD%98%E9%80%9A%E7%9F%A5%E4%BA%8B%E4%BB%B6.html&quot;&gt;win32 低内存通知事件&lt;/a&gt;
&lt;!-- [win32 低内存通知事件 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18263040 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/kybs0/p/18192224&quot;&gt;Windows 设置应用禁用卸载 - 唐宋元明清2188 - 博客园&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/kybs0/p/18330811#5293467&quot;&gt;.NET 窗口/屏幕录制 - 唐宋元明清2188 - 博客园&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E4%BD%BF%E7%94%A8-SHFileOperation-%E8%B0%83%E7%94%A8-Win32-%E7%9A%84%E6%96%87%E4%BB%B6%E5%A4%8D%E5%88%B6%E5%AF%B9%E8%AF%9D%E6%A1%86.html&quot;&gt;dotnet C# 使用 SHFileOperation 调用 Win32 的文件复制对话框&lt;/a&gt;
&lt;!-- [dotnet C# 使用 SHFileOperation 调用 Win32 的文件复制对话框 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18351897 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/czwy/p/17915333.html&quot;&gt;.NET中如何实现高精度定时器 - czwy - 博客园&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.gkarch.com/2015/09/high-resolution-timer.html&quot;&gt;高精度定时器实现 - GKarch&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Windows-%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%BD%AF%E4%BB%B6%E8%87%AA%E5%8A%A8%E6%9B%B4%E6%96%B0%E6%9C%8D%E5%8A%A1%E7%9A%84%E5%BC%80%E5%8F%91%E6%9C%89%E5%93%AA%E4%BA%9B%E9%9C%80%E6%B1%82.html&quot;&gt;Windows 客户端软件自动更新服务的开发有哪些需求&lt;/a&gt;
&lt;!-- [Windows 客户端软件自动更新服务的开发有哪些需求 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18426258 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AE%B0-QEMU-%E8%99%9A%E6%8B%9F%E7%A3%81%E7%9B%98%E8%AE%BE%E5%A4%87%E7%A7%BB%E5%8A%A8%E6%96%87%E4%BB%B6%E6%8A%9B%E5%BC%82%E5%B8%B8%E4%BD%86%E5%AE%9E%E9%99%85%E7%A7%BB%E5%8A%A8%E6%88%90%E5%8A%9F.html&quot;&gt;记 QEMU 虚拟磁盘设备移动文件抛异常但实际移动成功&lt;/a&gt;
&lt;!-- [记 QEMU 虚拟磁盘设备移动文件抛异常但实际移动成功 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18547263 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Windows-%E8%A1%8C%E4%B8%BA%E6%B5%8B%E8%AF%95-%E5%88%A0%E9%99%A4-FileStream-%E6%AD%A3%E5%9C%A8%E8%AF%BB%E5%86%99%E6%96%87%E4%BB%B6%E5%8F%AF%E4%BB%A5%E7%BB%A7%E7%BB%AD%E8%AF%BB%E5%86%99.html&quot;&gt;Windows 行为测试 删除 FileStream 正在读写文件可以继续读写&lt;/a&gt;
&lt;!-- [Windows 行为测试 删除 FileStream 正在读写文件可以继续读写 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18672096 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E7%AE%80%E5%8D%95%E9%80%9A%E8%BF%87-EnumDisplayDevices-%E8%8E%B7%E5%8F%96%E6%98%BE%E7%A4%BA%E8%AE%BE%E5%A4%87%E4%BF%A1%E6%81%AF.html&quot;&gt;dotnet 简单通过 EnumDisplayDevices 获取显示设备信息&lt;/a&gt;
&lt;!-- [dotnet 简单通过 EnumDisplayDevices 获取显示设备信息 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18856299 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%88%A9%E7%94%A8-Windows-%E6%B3%A8%E5%86%8C%E8%A1%A8%E5%AE%9E%E7%8E%B0%E5%BC%80%E6%9C%BA%E8%87%AA%E5%8A%A8%E5%90%AF%E5%8A%A8.html&quot;&gt;dotnet 利用 Windows 注册表实现开机自动启动&lt;/a&gt;
&lt;!-- [dotnet 利用 Windows 注册表实现开机自动启动 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19154026 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BD%BF%E7%94%A8-IShellLinkW-%E5%88%9B%E5%BB%BA-URL-%E7%BD%91%E5%9D%80%E8%B6%85%E9%93%BE%E6%8E%A5%E5%BF%AB%E6%8D%B7%E6%96%B9%E5%BC%8F.html&quot;&gt;使用 IShellLinkW 创建 URL 网址超链接快捷方式&lt;/a&gt;
&lt;!-- [使用 IShellLinkW 创建 URL 网址超链接快捷方式 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19407307 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Win32-%E4%BD%BF%E7%94%A8-MoveFileEx-%E5%BB%B6%E8%BF%9F%E5%88%B0%E9%87%8D%E5%90%AF%E5%90%8E%E5%88%A0%E9%99%A4%E6%96%87%E4%BB%B6.html&quot;&gt;Win32 使用 MoveFileEx 延迟到重启后删除文件&lt;/a&gt;
&lt;!-- [Win32 使用 MoveFileEx 延迟到重启后删除文件 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19572306 ) --&gt;&lt;/p&gt;

&lt;h4 id=&quot;wic&quot;&gt;WIC&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%9C%A8-Windows-%E7%B3%BB%E7%BB%9F%E4%B8%8A%E4%BD%BF%E7%94%A8-stakx-%E7%9A%84-WIC-%E5%BA%93.html&quot;&gt;dotnet 在 Windows 系统上使用 stakx 的 WIC 库&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-win32-%E4%BD%BF%E7%94%A8-WIC-%E8%8E%B7%E5%8F%96%E7%B3%BB%E7%BB%9F%E7%BC%96%E8%A7%A3%E7%A0%81%E5%99%A8.html&quot;&gt;dotnet win32 使用 WIC 获取系统编解码器&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E9%80%9A%E8%BF%87-GifBitmapDecoder-%E8%B0%83%E7%94%A8-WIC-%E8%A7%A3%E6%9E%90-Gif-%E5%92%8C%E8%BF%9B%E8%A1%8C%E5%8A%A8%E7%94%BB%E6%92%AD%E6%94%BE%E7%9A%84%E7%AE%80%E5%8D%95%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 通过 GifBitmapDecoder 调用 WIC 解析 Gif 和进行动画播放的简单方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%AF%B9%E6%8E%A5-Vortice-%E8%B0%83%E7%94%A8-WIC-%E5%8A%A0%E8%BD%BD%E5%9B%BE%E7%89%87.html&quot;&gt;WPF 对接 Vortice 调用 WIC 加载图片&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;uia&quot;&gt;UIA&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/pandefu/p/17536281.html&quot;&gt;UI自动化 — 微软UI Automation - NiueryDiary - 博客园&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/pandefu/p/17536282.html&quot;&gt;UI自动化 — UI Automation 基础详解 - NiueryDiary - 博客园&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95%E6%B3%A8%E5%85%A5%E6%96%87%E4%BB%B6%E8%AF%BB%E5%86%99.html&quot;&gt;dotnet 单元测试注入文件读写&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%A6%82%E4%BD%95%E5%9C%A8-dotnet-test-%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95%E6%8E%A7%E5%88%B6%E5%8F%B0%E9%87%8C%E8%BE%93%E5%87%BA%E6%97%A5%E5%BF%97%E5%86%85%E5%AE%B9.html&quot;&gt;dotnet 如何在 dotnet test 单元测试控制台里输出日志内容&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95-Mock-%E8%AE%A9%E4%B8%80%E4%B8%AA%E5%AF%B9%E8%B1%A1%E7%BB%A7%E6%89%BF%E5%A4%9A%E4%B8%AA%E6%8E%A5%E5%8F%A3.html&quot;&gt;dotnet 单元测试 Mock 让一个对象继承多个接口&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/win10-UWP-%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95.html&quot;&gt;win10 UWP 单元测试&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%9B%86%E6%88%90%E6%B5%8B%E8%AF%95-SmartSql-%E5%AD%98%E5%9C%A8%E9%9D%99%E6%80%81%E9%87%8F%E5%AF%BC%E8%87%B4%E5%A4%9A%E4%B8%AA%E4%B8%BB%E6%9C%BA%E5%90%AF%E5%8A%A8%E6%8F%90%E7%A4%BA-Alias-%E5%B7%B2%E5%AD%98%E5%9C%A8.html&quot;&gt;dotnet 集成测试 SmartSql 存在静态量导致多个主机启动提示 Alias 已存在&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/asp-dotnet-core-%E6%8F%90%E4%BE%9B%E5%A4%A7%E6%96%87%E4%BB%B6%E4%B8%8B%E8%BD%BD%E7%9A%84%E6%B5%8B%E8%AF%95.html&quot;&gt;asp dotnet core 提供大文件下载的测试&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/asp-dotnet-core-%E4%B8%8D%E6%AD%A3%E7%BB%8F%E7%9A%84%E6%8F%90%E5%8D%87%E6%95%88%E7%8E%87%E7%9A%84%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95%E6%96%B9%E6%B3%95.html&quot;&gt;asp dotnet core 不正经的提升效率的单元测试方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/asp-dotnet-core-%E5%9F%BA%E4%BA%8E-TestServer-%E5%81%9A%E9%9B%86%E6%88%90%E6%B5%8B%E8%AF%95.html&quot;&gt;asp dotnet core 基于 TestServer 做集成测试&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-MSTestRunner-%E5%B0%86%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95%E5%88%B6%E4%BD%9C%E4%B8%BA%E7%8B%AC%E7%AB%8B%E5%8F%AF%E6%89%A7%E8%A1%8C%E6%96%87%E4%BB%B6.html&quot;&gt;dotnet 使用 MSTestRunner 将单元测试制作为独立可执行文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%AE%9A%E5%88%B6-MSTest-%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95%E6%A1%86%E6%9E%B6-%E8%AE%A9%E4%B8%80%E4%B8%AA-TestMethod-%E5%8C%85%E5%90%AB%E5%A4%9A%E4%B8%AA%E6%B5%8B%E8%AF%95%E7%94%A8%E4%BE%8B.html&quot;&gt;定制 MSTest 单元测试框架 让一个 TestMethod 包含多个测试用例&lt;/a&gt;
&lt;!-- [定制 MSTest 单元测试框架 让一个 TestMethod 包含多个测试用例 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18993616 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;性能测试&quot;&gt;性能测试&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;p&gt;&lt;a href=&quot;/post/dotnet-6-%E6%95%B0%E7%BB%84%E6%8B%B7%E8%B4%9D%E6%80%A7%E8%83%BD%E5%AF%B9%E6%AF%94.html&quot;&gt;dotnet 6 数组拷贝性能对比&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%AD%97%E5%85%B8-Dictionary-%E5%92%8C-Hashtable-%E7%9A%84%E6%80%A7%E8%83%BD%E5%AF%B9%E6%AF%94.html&quot;&gt;dotnet C# 字典 Dictionary 和 Hashtable 的性能对比&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-5-%E9%9D%99%E6%80%81%E5%AD%97%E6%AE%B5%E5%92%8C%E5%B1%9E%E6%80%A7%E7%9A%84%E5%8F%8D%E5%B0%84%E8%8E%B7%E5%8F%96-%E6%B2%A1%E6%9C%89%E6%83%B3%E8%B1%A1%E4%B8%AD%E9%82%A3%E4%B9%88%E4%BC%A4%E6%80%A7%E8%83%BD.html&quot;&gt;dotnet 5 静态字段和属性的反射获取 没有想象中那么伤性能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E9%93%BE%E8%A1%A8%E5%92%8C%E5%AD%97%E5%85%B8%E7%9A%84%E6%80%A7%E8%83%BD%E5%AF%B9%E6%AF%94.html&quot;&gt;dotnet C# 链表和字典的性能对比&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E7%A8%8B%E5%BA%8F%E5%86%85%E7%9A%84%E7%B1%BB%E6%95%B0%E9%87%8F%E5%AF%B9%E7%A8%8B%E5%BA%8F%E5%90%AF%E5%8A%A8%E7%9A%84%E5%BD%B1%E5%93%8D.html&quot;&gt;C# 程序内的类数量对程序启动的影响&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E5%AD%98%E5%82%A8-%E5%90%84%E7%A7%8D%E5%BA%8F%E5%88%97%E5%8C%96%E7%AE%97%E6%B3%95%E6%80%A7%E8%83%BD%E6%AF%94%E8%BE%83.html&quot;&gt;C# 配置文件存储 各种序列化算法性能比较&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E5%AD%97%E5%85%B8-Dictionary-%E7%9A%84-TryGetValue-%E4%B8%8E%E5%85%88%E5%88%A4%E6%96%AD-ContainsKey-%E7%84%B6%E5%90%8E-Get-%E7%9A%84%E6%80%A7%E8%83%BD%E5%AF%B9%E6%AF%94.html&quot;&gt;C# 字典 Dictionary 的 TryGetValue 与先判断 ContainsKey 然后 Get 的性能对比&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-ConcurrentDictionary-%E7%9A%84-GetOrAdd-%E6%80%A7%E8%83%BD%E6%AF%94-TryGetValue-%E5%8A%A0-TryAdd-%E4%BD%8E.html&quot;&gt;dotnet ConcurrentDictionary 的 GetOrAdd 性能比 TryGetValue 加 TryAdd 低&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%8A%A8%E7%94%BB%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E5%BA%94%E7%94%A8-%E4%B8%80%E5%8D%83%E4%B8%AA%E5%8D%8A%E9%80%8F%E6%98%8E%E7%9F%A9%E5%BD%A2%E5%81%9A%E5%8A%A8%E7%94%BB.html&quot;&gt;WPF 动画性能测试应用 一千个半透明矩形做动画&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E5%9C%A8%E4%B8%8D%E5%90%8C%E7%9A%84%E6%9C%BA%E5%99%A8-CPU-%E5%9E%8B%E5%8F%B7%E4%B8%8A%E7%9A%84%E5%9F%BA%E5%87%86%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95.html&quot;&gt;dotnet C# 在不同的机器 CPU 型号上的基准性能测试&lt;/a&gt;
&lt;!-- [dotnet C# 在不同的机器 CPU 型号上的基准性能测试 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18674065 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%AF%B9%E4%B8%80%E4%BA%9B-Win32-%E6%96%B9%E6%B3%95%E8%BF%9B%E8%A1%8C-Benchmark-%E5%9F%BA%E5%87%86%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95.html&quot;&gt;dotnet 对一些 Win32 方法进行 Benchmark 基准性能测试&lt;/a&gt;
&lt;!-- [dotnet 对一些 Win32 方法进行 Benchmark 基准性能测试 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18674064 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;测试工具&quot;&gt;测试工具&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E8%87%AA%E5%8A%A8%E7%BF%BB%E9%A1%B5-PPT-%E6%B5%8B%E8%AF%95%E8%84%9A%E6%9C%AC.html&quot;&gt;C# 自动翻页 PPT 测试脚本&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%88%B6%E4%BD%9C%E4%B8%80%E4%B8%AA%E5%8D%A0%E7%94%A8%E6%96%87%E4%BB%B6%E7%9A%84%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7.html&quot;&gt;WPF 制作一个占用文件的测试工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/GitHub-%E7%9A%84-Action-%E6%8E%A5%E5%85%A5-Stryker.NET-%E8%BF%9B%E8%A1%8C%E8%87%AA%E5%8A%A8%E5%8C%96%E6%B5%8B%E8%AF%95%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95%E9%B2%81%E6%A3%92%E6%80%A7.html&quot;&gt;GitHub 的 Action 接入 Stryker.NET 进行自动化测试单元测试鲁棒性&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;测试框架&quot;&gt;测试框架&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnetCampus.UITest.WPF-%E4%B8%80%E4%B8%AA%E6%94%AF%E6%8C%81%E4%B8%AD%E6%96%87%E7%94%A8%E4%BE%8B%E7%9A%84%E7%95%8C%E9%9D%A2%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95%E6%A1%86%E6%9E%B6.html&quot;&gt;dotnetCampus.UITest.WPF 一个支持中文用例的界面单元测试框架&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%A6%82%E4%BD%95%E5%9C%A8-Mock-%E6%A8%A1%E6%8B%9F-Func-%E5%88%A4%E6%96%AD%E8%B0%83%E7%94%A8%E6%AC%A1%E6%95%B0.html&quot;&gt;dotnet 如何在 Mock 模拟 Func 判断调用次数&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Moq%E5%9F%BA%E7%A1%80-%E5%88%A4%E6%96%AD%E6%96%B9%E6%B3%95%E8%A2%AB%E6%89%A7%E8%A1%8C.html&quot;&gt;Moq基础 判断方法被执行&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;autofac-集成测试&quot;&gt;Autofac 集成测试&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/Autofac-%E9%80%9A%E8%BF%87-PreserveExistingDefaults-%E8%A7%A3%E5%86%B3%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95-Fake-%E5%AF%B9%E8%B1%A1%E8%A2%AB%E8%A6%86%E7%9B%96.html&quot;&gt;Autofac 通过 PreserveExistingDefaults 解决单元测试 Fake 对象被覆盖&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Autofac-%E9%9B%86%E6%88%90%E6%B5%8B%E8%AF%95-%E5%9C%A8-ConfigureContainer-%E4%B9%8B%E5%90%8E%E8%BF%9B%E8%A1%8C-Mock-%E6%B3%A8%E5%85%A5.html&quot;&gt;Autofac 集成测试 在 ConfigureContainer 之后进行 Mock 注入&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;我写了很多关于 Roslyn 相关的博客，请参阅：&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/roslyn.html&quot;&gt;手把手教你写 Roslyn 修改编译博客导航&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;前置知识&quot;&gt;前置知识&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.walterlv.com/post/understand-the-csproj&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程 - walterlv&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/hez2010/p/a-brand-new-look-at-msbuild-1.html&quot;&gt;重新认识 MSBuild - 1 - hez2010 - 博客园&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;构建的基础知识&quot;&gt;构建的基础知识&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-%E5%A6%82%E4%BD%95%E4%BA%86%E8%A7%A3%E6%9F%90%E4%B8%AA%E9%A1%B9%E7%9B%AE%E5%9C%A8-msbuild-%E4%B8%AD%E6%89%80%E6%9C%89%E7%94%A8%E5%88%B0%E7%9A%84%E5%B1%9E%E6%80%A7%E4%BB%A5%E5%8F%8A%E6%9E%84%E5%BB%BA%E8%BF%87%E7%A8%8B.html&quot;&gt;Roslyn 如何了解某个项目在 msbuild 中所有用到的属性以及构建过程&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.walterlv.com/post/known-properties-in-csproj.html&quot;&gt;项目文件中的已知属性（知道了这些，就不会随便在 csproj 中写死常量啦） - walterlv&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/msbuild-%E9%A1%B9%E7%9B%AE%E6%96%87%E4%BB%B6%E5%B8%B8%E7%94%A8%E5%88%A4%E6%96%AD%E6%9D%A1%E4%BB%B6.html&quot;&gt;msbuild 项目文件常用判断条件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-%E5%88%A4%E6%96%AD%E5%BD%93%E5%89%8D%E4%BD%BF%E7%94%A8-dotnet-core-%E7%BC%96%E8%AF%91%E5%99%A8%E8%BF%9B%E8%A1%8C%E7%BC%96%E8%AF%91.html&quot;&gt;Roslyn 判断当前使用 dotnet core 编译器进行编译&lt;/a&gt;&lt;/p&gt;

&lt;h5 id=&quot;分析器入门知识&quot;&gt;分析器入门知识&lt;/h5&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90%E5%99%A8%E5%88%86%E6%9E%90%E5%99%A8%E5%85%A5%E9%97%A8.html&quot;&gt;dotnet 源代码生成器分析器入门&lt;/a&gt;
&lt;!-- [dotnet 源代码生成器分析器入门 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18786647 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.walterlv.com/post/roslyn-syntax-visualizer&quot;&gt;Roslyn 入门：使用 Visual Studio 的语法可视化（Syntax Visualizer）窗格查看和了解代码的语法树 - walterlv&lt;/a&gt;
&lt;!-- [Roslyn 入门：使用 Visual Studio 的语法可视化窗格查看和了解代码的语法树 - walterlv - 博客园](https://www.cnblogs.com/walterlv/p/10236479.html ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.walterlv.com/post/compile-and-invoke-code-using-roslyn.html&quot;&gt;Roslyn 入门：使用 .NET Core 版本的 Roslyn 编译并执行跨平台的静态的源码 - walterlv&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.walterlv.com/post/analysis-code-of-existed-projects-using-roslyn.html&quot;&gt;Roslyn 入门：使用 Roslyn 静态分析现有项目中的代码（语法分析） - walterlv&lt;/a&gt;
&lt;!-- [Roslyn 入门：使用 Roslyn 静态分析现有项目中的代码 - walterlv - 博客园](https://www.cnblogs.com/walterlv/p/10236480.html ) --&gt;&lt;/p&gt;

&lt;h5 id=&quot;roslyn-基础知识&quot;&gt;Roslyn 基础知识&lt;/h5&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-%E5%88%86%E6%9E%90%E5%99%A8-EnforceExtendedAnalyzerRules-%E5%B1%9E%E6%80%A7%E7%9A%84%E4%BD%9C%E7%94%A8.html&quot;&gt;Roslyn 分析器 EnforceExtendedAnalyzerRules 属性的作用&lt;/a&gt;
&lt;!-- [Roslyn 分析器 EnforceExtendedAnalyzerRules 属性的作用 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/17678673.html ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-Roslyn-%E9%80%9A%E8%BF%87%E8%AF%BB%E5%8F%96-suo-%E6%96%87%E4%BB%B6%E8%8E%B7%E5%8F%96%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88%E7%9A%84%E5%90%AF%E5%8A%A8%E9%A1%B9%E7%9B%AE.html&quot;&gt;dotnet Roslyn 通过读取 suo 文件获取解决方案的启动项目&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-%E9%9D%99%E6%80%81%E5%88%86%E6%9E%90.html&quot;&gt;Roslyn 静态分析&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-%E5%A6%82%E4%BD%95%E8%8E%B7%E5%BE%97%E4%B8%80%E4%B8%AA%E7%B1%BB%E7%9A%84%E5%BC%95%E7%94%A8.html&quot;&gt;Roslyn 如何获得一个类的引用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-%E7%AE%80%E5%8D%95%E5%AE%9E%E7%8E%B0%E4%BB%A3%E7%A0%81%E6%99%BA%E8%83%BD%E6%8F%90%E7%A4%BA%E8%A1%A5%E5%85%A8%E5%8A%9F%E8%83%BD.html&quot;&gt;Roslyn 简单实现代码智能提示补全功能&lt;/a&gt;
&lt;!-- [Roslyn 简单实现代码智能提示补全功能 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18365261 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90%E5%99%A8-SourceGenerator-%E8%8E%B7%E5%8F%96%E4%BB%A3%E7%A0%81%E6%96%87%E4%BB%B6%E7%9A%84%E6%9C%AC%E5%9C%B0%E7%BB%9D%E5%AF%B9%E8%B7%AF%E5%BE%84.html&quot;&gt;Roslyn 源代码生成器 SourceGenerator 获取代码文件的本地绝对路径&lt;/a&gt;
&lt;!-- [Roslyn 源代码生成器 SourceGenerator 获取代码文件的本地绝对路径 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18705712 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-%E5%88%86%E6%9E%90%E5%99%A8-INamedTypeSymbol-%E7%9A%84-AllInterfaces-%E5%8C%85%E5%90%AB%E7%B1%BB%E5%9E%8B%E5%85%A8%E9%83%A8%E7%BB%A7%E6%89%BF%E6%8E%A5%E5%8F%A3.html&quot;&gt;Roslyn 分析器 INamedTypeSymbol 的 AllInterfaces 包含类型全部继承接口&lt;/a&gt;
&lt;!-- [Roslyn 分析器 INamedTypeSymbol 的 AllInterfaces 包含类型全部继承接口 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18826021 ) --&gt;&lt;/p&gt;

&lt;h4 id=&quot;构建编排&quot;&gt;构建编排&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.walterlv.com/post/write-msbuild-target.html&quot;&gt;如何编写基于 Microsoft.NET.Sdk 的跨平台的 MSBuild Target（附各种自带的 Task） - walterlv&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-%E8%AE%A9%E7%BC%96%E8%AF%91%E6%97%B6%E5%80%99-Message-%E5%86%85%E5%AE%B9%E9%BB%98%E8%AE%A4%E8%BE%93%E5%87%BA.html&quot;&gt;Roslyn 让编译时候 Message 内容默认输出&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-%E7%90%86%E8%A7%A3-msbuild-%E7%9A%84%E6%B8%85%E7%90%86%E8%BF%87%E7%A8%8B.html&quot;&gt;Roslyn 理解 msbuild 的清理过程&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-%E8%8E%B7%E5%BE%97-sln-%E6%96%87%E4%BB%B6%E6%89%80%E5%9C%A8%E7%9A%84%E6%96%87%E4%BB%B6%E5%A4%B9.html&quot;&gt;Roslyn 获得 sln 文件所在的文件夹&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8-MSBuild-MakeDir-%E5%88%9B%E5%BB%BA%E6%96%87%E4%BB%B6%E5%A4%B9.html&quot;&gt;Roslyn 如何使用 MSBuild MakeDir 创建文件夹&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-%E5%9C%A8%E5%A4%9A%E5%BC%80%E5%8F%91%E6%A1%86%E6%9E%B6%E8%AE%A9-msbuild-%E7%9A%84-Target-%E4%BB%85%E8%BF%90%E8%A1%8C%E4%B8%80%E6%AC%A1.html&quot;&gt;Roslyn 在多开发框架让 msbuild 的 Target 仅运行一次&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-%E7%A6%81%E6%AD%A2-sdk-style-csproj-%E9%BB%98%E8%AE%A4%E5%BC%95%E7%94%A8-Compile-%E4%BB%A3%E7%A0%81%E6%96%87%E4%BB%B6.html&quot;&gt;Roslyn 禁止 sdk style csproj 默认引用 Compile 代码文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;h4 id=&quot;定制构建&quot;&gt;定制构建&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%80%9A%E8%BF%87%E5%BC%95%E7%94%A8-msbuild-%E7%A8%8B%E5%BA%8F%E9%9B%86%E5%AE%9E%E7%8E%B0%E8%87%AA%E5%B7%B1%E5%AE%9A%E5%88%B6%E7%BC%96%E8%AF%91%E5%99%A8.html&quot;&gt;dotnet 通过引用 msbuild 程序集实现自己定制编译器&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%A1%86%E6%9E%B6%E5%BC%80%E5%8F%91-%E8%B0%83%E8%AF%95%E5%92%8C%E5%BC%80%E5%8F%91-XAML-%E6%9E%84%E5%BB%BA%E8%BF%87%E7%A8%8B%E7%9A%84-PresentationBuildTasks-%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 框架开发 调试和开发 XAML 构建过程的 PresentationBuildTasks 方法&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;roslyn-应用&quot;&gt;Roslyn 应用&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-MSBuild-%E5%9C%A8%E6%9E%84%E5%BB%BA%E5%AE%8C%E6%88%90%E4%B9%8B%E5%90%8E-%E5%B0%86%E6%9E%84%E5%BB%BA%E6%97%B6%E9%97%B4%E5%86%99%E5%85%A5%E5%88%B0%E8%BE%93%E5%87%BA%E6%96%87%E4%BB%B6.html&quot;&gt;Roslyn MSBuild 在构建完成之后 将构建时间写入到输出文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-%E5%B0%86%E8%BF%99%E4%B8%AA%E6%96%87%E4%BB%B6%E6%94%BE%E5%9C%A8%E4%BD%A0%E7%9A%84%E9%A1%B9%E7%9B%AE%E6%96%87%E4%BB%B6%E5%A4%B9-%E6%97%A0%E8%AE%BA%E5%93%AA%E4%B8%AA%E6%8E%A7%E5%88%B6%E5%8F%B0%E9%A1%B9%E7%9B%AE%E9%83%BD%E4%BC%9A%E8%BE%93%E5%87%BA%E6%9E%97%E5%BE%B7%E7%86%99%E6%98%AF%E9%80%97%E6%AF%94.html&quot;&gt;Roslyn 将这个文件放在你的项目文件夹，无论哪个控制台项目都会输出林德熙是逗比&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;已知问题-1&quot;&gt;已知问题&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-%E5%88%86%E6%9E%90%E5%99%A8%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E4%BC%A0%E9%80%92%E9%A1%B9%E7%9B%AE%E5%B1%9E%E6%80%A7%E6%97%B6%E5%B0%86%E5%BF%BD%E7%95%A5%E5%88%86%E5%8F%B7%E4%B9%8B%E5%90%8E%E7%9A%84%E5%86%85%E5%AE%B9.html&quot;&gt;Roslyn 分析器已知问题 传递项目属性时将忽略分号之后的内容&lt;/a&gt;
&lt;!-- [Roslyn 分析器已知问题 传递项目属性时将忽略分号之后的内容 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18766604 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;生成代码&quot;&gt;生成代码&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90%E5%99%A8%E5%88%86%E6%9E%90%E5%99%A8%E5%85%A5%E9%97%A8.html&quot;&gt;dotnet 源代码生成器分析器入门&lt;/a&gt;
&lt;!-- [dotnet 源代码生成器分析器入门 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18786647 ) --&gt;&lt;/p&gt;

&lt;p&gt;大佬收藏的 Source Generators 列表： &lt;a href=&quot;https://github.com/amis92/csharp-source-generators&quot;&gt;amis92/csharp-source-generators: A list of C# Source Generators (not necessarily awesome) and associated resources: articles, talks, demos.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%B0%9D%E8%AF%95-IIncrementalGenerator-%E8%BF%9B%E8%A1%8C%E5%A2%9E%E9%87%8F-Source-Generator-%E7%94%9F%E6%88%90%E4%BB%A3%E7%A0%81.html&quot;&gt;尝试 IIncrementalGenerator 进行增量 Source Generator 生成代码&lt;/a&gt;
&lt;!-- [尝试 IIncrementalGenerator 进行增量 Source Generator 生成代码 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/16697794.html ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%B8%BA-IIncrementalGenerator-%E5%A2%9E%E9%87%8F-Source-Generator-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90%E9%A1%B9%E7%9B%AE%E6%B7%BB%E5%8A%A0%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95.html&quot;&gt;为 IIncrementalGenerator 增量 Source Generator 源代码生成项目添加单元测试&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/IIncrementalGenerator-%E5%A6%82%E4%BD%95%E5%9C%A8%E6%BA%90%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90%E5%99%A8%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95%E6%8F%90%E4%BE%9B-AnalyzerConfigOptionsProvider-%E9%80%89%E9%A1%B9.html&quot;&gt;IIncrementalGenerator 如何在源代码生成器单元测试提供 AnalyzerConfigOptionsProvider 选项&lt;/a&gt;
&lt;!-- [IIncrementalGenerator 如何在源代码生成器单元测试提供 AnalyzerConfigOptionsProvider 选项 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19275194 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%9C%A8-VisualStudio-%E4%B8%80%E9%94%AE-F5-%E5%90%AF%E5%8A%A8%E8%B0%83%E8%AF%95-Roslyn-%E5%88%86%E6%9E%90%E5%99%A8%E9%A1%B9%E7%9B%AE.html&quot;&gt;dotnet 在 VisualStudio 一键 F5 启动调试 Roslyn 分析器项目&lt;/a&gt;
&lt;!-- [dotnet 在 VisualStudio 一键 F5 启动调试 Roslyn 分析器项目 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18730521 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-IndentedTextWriter-%E8%BE%85%E5%8A%A9%E7%94%9F%E6%88%90%E4%BB%A3%E7%A0%81%E6%97%B6%E7%94%9F%E6%88%90%E5%B8%A6%E7%BC%A9%E8%BF%9B%E7%9A%84%E5%86%85%E5%AE%B9.html&quot;&gt;dotnet 使用 IndentedTextWriter 辅助生成代码时生成带缩进的内容&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/IIncrementalGenerator-%E5%A2%9E%E9%87%8F-Source-Generator-%E7%94%9F%E6%88%90%E4%BB%A3%E7%A0%81%E5%85%A5%E9%97%A8-%E8%AF%BB%E5%8F%96-csproj-%E9%A1%B9%E7%9B%AE%E6%96%87%E4%BB%B6%E7%9A%84%E5%B1%9E%E6%80%A7%E9%85%8D%E7%BD%AE.html&quot;&gt;IIncrementalGenerator 增量 Source Generator 生成代码入门 读取 csproj 项目文件的属性配置&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-%E5%88%86%E6%9E%90%E5%99%A8-%E8%AF%BB%E5%8F%96-csproj-%E9%A1%B9%E7%9B%AE%E6%96%87%E4%BB%B6%E7%9A%84-AdditionalFiles-Item-%E7%9A%84-Metadata-%E9%85%8D%E7%BD%AE.html&quot;&gt;Roslyn 分析器 读取 csproj 项目文件的 AdditionalFiles Item 的 Metadata 配置&lt;/a&gt;
&lt;!-- [Roslyn 分析器 读取 csproj 项目文件的 AdditionalFiles Item 的 Metadata 配置 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18459703 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/IIncrementalGenerator-%E5%A2%9E%E9%87%8F-Source-Generator-%E7%94%9F%E6%88%90%E4%BB%A3%E7%A0%81%E5%85%A5%E9%97%A8-%E8%8E%B7%E5%8F%96%E5%BC%95%E7%94%A8%E7%A8%8B%E5%BA%8F%E9%9B%86%E7%9A%84%E6%89%80%E6%9C%89%E7%B1%BB%E5%9E%8B.html&quot;&gt;IIncrementalGenerator 增量 Source Generator 生成代码入门 获取引用程序集的所有类型&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/IIncrementalGenerator-%E5%A2%9E%E9%87%8F-Source-Generator-%E7%94%9F%E6%88%90%E4%BB%A3%E7%A0%81%E5%85%A5%E9%97%A8-%E5%88%A4%E6%96%AD%E7%A8%8B%E5%BA%8F%E9%9B%86%E4%B9%8B%E9%97%B4%E7%9A%84-InternalsVisibleTo-%E5%85%B3%E7%B3%BB.html&quot;&gt;IIncrementalGenerator 增量 Source Generator 生成代码入门 判断程序集之间的 InternalsVisibleTo 关系&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/IIncrementalGenerator-%E5%A2%9E%E9%87%8F-Source-Generator-%E7%94%9F%E6%88%90%E4%BB%A3%E7%A0%81%E5%85%A5%E9%97%A8-%E5%88%A4%E6%96%AD%E7%A8%8B%E5%BA%8F%E9%9B%86%E7%9A%84%E5%BC%95%E7%94%A8%E5%85%B3%E7%B3%BB.html&quot;&gt;IIncrementalGenerator 增量 Source Generator 生成代码入门 判断程序集的引用关系&lt;/a&gt;
&lt;!-- [IIncrementalGenerator 判断程序集的引用关系 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/17678664.html ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/IIncrementalGenerator-%E5%A2%9E%E9%87%8F-Source-Generator-%E7%94%9F%E6%88%90%E4%BB%A3%E7%A0%81%E5%85%A5%E9%97%A8-%E8%8E%B7%E5%8F%96%E9%A1%B9%E7%9B%AE%E9%BB%98%E8%AE%A4%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4.html&quot;&gt;IIncrementalGenerator 增量 Source Generator 生成代码入门 获取项目默认命名空间&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/IIncrementalGenerator-%E5%A2%9E%E9%87%8F-Source-Generator-%E7%94%9F%E6%88%90%E4%BB%A3%E7%A0%81%E5%85%A5%E9%97%A8-%E8%AF%BB%E5%8F%96%E8%A7%A3%E6%9E%90-ValueTuple-%E7%9A%84%E5%AE%9A%E4%B9%89.html&quot;&gt;IIncrementalGenerator 增量 Source Generator 生成代码入门 读取解析 ValueTuple 的定义&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BD%BF%E7%94%A8-ForAttributeWithMetadataName-%E6%8F%90%E9%AB%98-IIncrementalGenerator-%E5%A2%9E%E9%87%8F-Source-Generator-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90%E5%BC%80%E5%8F%91%E6%95%88%E7%8E%87%E5%92%8C%E6%80%A7%E8%83%BD.html&quot;&gt;使用 ForAttributeWithMetadataName 提高 IIncrementalGenerator 增量 Source Generator 源代码生成开发效率和性能&lt;/a&gt;
&lt;!-- [使用 ForAttributeWithMetadataName 提高 IIncrementalGenerator 增量 Source Generator 源代码生成开发效率和性能 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18009107 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%B0%86-Source-Generator-%E7%94%9F%E6%88%90%E7%9A%84%E6%BA%90%E4%BB%A3%E7%A0%81%E4%BF%9D%E5%AD%98%E5%88%B0%E6%9C%AC%E5%9C%B0%E6%96%87%E4%BB%B6.html&quot;&gt;将 Source Generator 生成的源代码保存到本地文件&lt;/a&gt;
&lt;!-- [将 Source Generator 生成的源代码保存到本地文件 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18011557 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/IIncrementalGenerator-%E5%A2%9E%E9%87%8F-Source-Generator-%E7%94%9F%E6%88%90%E4%BB%A3%E7%A0%81%E5%BA%94%E7%94%A8-%E5%B0%86%E6%9E%84%E5%BB%BA%E6%97%B6%E9%97%B4%E5%86%99%E5%85%A5%E6%BA%90%E4%BB%A3%E7%A0%81.html&quot;&gt;IIncrementalGenerator 增量 Source Generator 生成代码应用 将构建时间写入源代码&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E7%94%A8-SourceGenerator-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E7%8E%B0%E4%B8%AD%E6%96%87%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80.html&quot;&gt;dotnet 用 SourceGenerator 源代码生成技术实现中文编程语言&lt;/a&gt; &lt;a href=&quot;https://www.cnblogs.com/lindexi/p/16804899.html&quot;&gt;博客园&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;生成代码例子&quot;&gt;生成代码例子&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.walterlv.com/post/generate-csharp-source-using-roslyn-source-generator&quot;&gt;使用 Source Generator 在编译你的 .NET 项目时自动生成代码 - walterlv&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;visualstudio&quot;&gt;VisualStudio&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E5%90%84%E7%89%88%E6%9C%AC-msbuild-%E8%B7%AF%E5%BE%84.html&quot;&gt;VisualStudio 各版本 msbuild 路径&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/visual-studio-2015-warning-MSB3246.html&quot;&gt;visual studio 2015 warning MSB3246&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Visual-studio-%E5%88%9B%E5%BB%BA%E9%A1%B9%E7%9B%AE%E5%A4%B1%E8%B4%A5vstemplate.html&quot;&gt;Visual studio 创建项目失败vstemplate&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Visual-Studio-%E8%87%AA%E5%AE%9A%E4%B9%89%E9%A1%B9%E7%9B%AE%E6%A8%A1%E6%9D%BF.html&quot;&gt;Visual Studio 自定义项目模板&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Visual-studio-C-%E4%BB%A3%E7%A0%81%E4%BD%BF%E7%94%A8-NotNull.html&quot;&gt;Visual studio C# 代码使用 NotNull&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;a href=&quot;/post/msbuild-Roslyn-%E8%A1%8C%E4%B8%BA%E8%AF%A6%E8%A7%A3.html&quot;&gt;msbuild Roslyn 行为详解&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-can-not-get-the-correct-struct-object-value-in-Locals-windows.html&quot;&gt;VisualStudio can not get the correct struct object value in Locals windows&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E8%87%AA%E5%AE%9A%E4%B9%89%E5%A4%96%E9%83%A8%E5%91%BD%E4%BB%A4.html&quot;&gt;VisualStudio 自定义外部命令&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E5%A4%96%E9%83%A8%E5%B7%A5%E5%85%B7%E9%85%8D%E5%90%88-dotnet-tool-%E5%88%B6%E4%BD%9C%E5%A4%8D%E5%88%B6%E6%96%87%E4%BB%B6%E5%90%8D%E5%B7%A5%E5%85%B7.html&quot;&gt;VisualStudio 外部工具配合 dotnet tool 制作复制文件名工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E5%A6%82%E4%BD%95%E5%BF%AB%E9%80%9F%E6%B7%BB%E5%8A%A0%E4%B8%80%E4%B8%AA-Git-Tag-%E6%8E%A8%E9%80%81.html&quot;&gt;VisualStudio 如何快速添加一个 Git Tag 推送&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/TotalCommander-%E8%AE%BE%E7%BD%AE-VisualStudio-%E5%BC%80%E5%8F%91%E8%80%85%E5%91%BD%E4%BB%A4%E8%A1%8C.html&quot;&gt;TotalCommander 设置 VisualStudio 开发者命令行&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E5%91%BD%E4%BB%A4%E8%A1%8C%E7%BC%96%E8%AF%91-build-%E9%80%9A%E8%BF%87-rebuild-%E4%B8%8D%E9%80%9A%E8%BF%87.html&quot;&gt;VisualStudio 命令行编译 build 通过 rebuild 不通过&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/msbuild-%E4%BF%AE%E6%94%B9-VisualStudio-%E6%96%87%E4%BB%B6%E5%A4%8D%E5%88%B6%E5%88%B0%E8%BE%93%E5%87%BA%E7%9B%AE%E5%BD%95%E7%9A%84%E8%B7%AF%E5%BE%84.html&quot;&gt;msbuild 修改 VisualStudio 文件复制到输出目录的路径&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%80%9A%E8%BF%87-DockerfileContext-%E8%A7%A3%E5%86%B3%E9%A1%B9%E7%9B%AE%E6%94%BE%E5%9C%A8%E9%87%8C%E5%B1%82%E6%96%87%E4%BB%B6%E5%A4%B9%E5%AF%BC%E8%87%B4-VisualStudio-%E6%9E%84%E5%BB%BA%E5%A4%B1%E8%B4%A5.html&quot;&gt;dotnet 通过 DockerfileContext 解决项目放在里层文件夹导致 VisualStudio 构建失败&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%9C%A8-VisualStudio-%E7%BB%99%E6%96%87%E4%BB%B6%E8%B5%B7%E4%B8%80%E4%B8%AA%E5%B8%A6%E5%88%86%E5%8F%B7%E7%9A%84%E6%96%87%E4%BB%B6%E5%90%8D%E4%BC%9A%E6%80%8E%E6%A0%B7.html&quot;&gt;在 VisualStudio 给文件起一个带分号的文件名会怎样&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E5%BC%80%E5%8F%91%E6%96%87%E4%BB%B6%E8%87%AA%E5%AE%9A%E4%B9%89%E5%B7%A5%E5%85%B7%E5%8D%95%E6%96%87%E4%BB%B6%E7%94%9F%E6%88%90%E5%B7%A5%E5%85%B7.html&quot;&gt;VisualStudio 开发文件自定义工具单文件生成工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-2019-%E6%96%B0%E7%89%B9%E6%80%A7.html&quot;&gt;VisualStudio 2019 新特性&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-2019-%E5%B0%9D%E8%AF%95%E4%BD%BF%E7%94%A8-C-8.0-%E6%96%B0%E7%9A%84%E6%96%B9%E5%BC%8F.html&quot;&gt;VisualStudio 2019 尝试使用 C# 8.0 新的方式&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-2017-%E9%A1%B9%E7%9B%AE%E6%A0%BC%E5%BC%8F-%E8%87%AA%E5%8A%A8%E7%94%9F%E6%88%90%E7%89%88%E6%9C%AC%E5%8F%B7.html&quot;&gt;VisualStudio 2017 项目格式 自动生成版本号&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%9C%A8-Windows-Defender-%E8%AE%BE%E7%BD%AE%E6%96%87%E4%BB%B6%E5%A4%B9%E7%99%BD%E5%90%8D%E5%8D%95%E6%8F%90%E5%8D%87-VisualStudio-%E7%BC%96%E8%AF%91%E9%80%9F%E5%BA%A6.html&quot;&gt;在 Windows Defender 设置文件夹白名单提升 VisualStudio 编译速度&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BD%BF%E7%94%A8-IncrediBuild-%E6%8F%90%E5%8D%87-VisualStudio-%E7%BC%96%E8%AF%91%E9%80%9F%E5%BA%A6.html&quot;&gt;使用 IncrediBuild 提升 VisualStudio 编译速度&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%AE%89%E8%A3%85visualStudio-%E5%87%BA%E7%8E%B0-cant-install-Microsoft.TeamFoundation.OfficeIntegration.Resources.html&quot;&gt;安装visualStudio 出现 cant install Microsoft.TeamFoundation.OfficeIntegration.Resources&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%B8%80%E6%AE%B5%E8%83%BD%E8%AE%A9-VisualStudio-%E7%82%B8%E6%8E%89%E7%9A%84%E4%BB%A3%E7%A0%81.html&quot;&gt;一段能让 VisualStudio 炸掉的代码&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/visualStudio-%E6%97%A0%E6%B3%95%E7%99%BB%E9%99%86.html&quot;&gt;visualStudio 无法登陆&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E7%A6%81%E7%94%A8%E7%A7%BB%E5%8A%A8%E6%96%87%E4%BB%B6%E5%88%B0%E6%96%87%E4%BB%B6%E5%A4%B9%E8%87%AA%E5%8A%A8%E4%BF%AE%E6%94%B9%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4%E5%8A%9F%E8%83%BD.html&quot;&gt;VisualStudio 禁用移动文件到文件夹自动修改命名空间功能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88%E7%AD%9B%E9%80%89%E5%99%A8-slnf-%E6%96%87%E4%BB%B6.html&quot;&gt;VisualStudio 解决方案筛选器 slnf 文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;p&gt;&lt;a href=&quot;/post/VisualStudio-2019-%E5%A6%82%E4%BD%95%E7%A6%BB%E7%BA%BF%E4%B8%8B%E8%BD%BD.html&quot;&gt;VisualStudio 2019 如何离线下载&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E9%85%8D%E7%BD%AE%E5%A4%9A%E8%BF%9B%E7%A8%8B%E8%B0%83%E8%AF%95%E5%BF%AB%E6%8D%B7%E9%94%AE%E5%90%AF%E5%8A%A8%E9%A1%B9%E7%9B%AE.html&quot;&gt;VisualStudio 配置多进程调试快捷键启动项目&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-2019-%E6%96%B0%E5%88%9B%E5%BB%BA%E9%A1%B9%E7%9B%AE%E6%B7%BB%E5%8A%A0-git-%E4%BB%93%E5%BA%93.html&quot;&gt;VisualStudio 2019 新创建项目添加 git 仓库&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-2022-%E5%A6%82%E4%BD%95%E6%98%BE%E7%A4%BA-dotnet-6-%E5%8F%8A%E4%BB%A5%E4%B8%8A%E7%89%88%E6%9C%AC%E7%9A%84%E6%A1%86%E6%9E%B6%E7%9A%84%E4%BB%A3%E7%A0%81%E6%B3%A8%E9%87%8A%E4%B8%BA%E4%B8%AD%E6%96%87.html&quot;&gt;VisualStudio 2022 如何显示 dotnet 6 及以上版本的框架的代码注释为中文&lt;/a&gt;
&lt;!-- [VisualStudio 2022 如何显示 dotnet 6 及以上版本的框架的代码注释为中文 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/17973889 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E5%BF%AB%E9%80%9F%E8%AE%BE%E7%BD%AE%E5%90%AF%E5%8A%A8%E9%A1%B9%E7%9B%AE.html&quot;&gt;VisualStudio 快速设置启动项目&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E5%90%88%E5%B9%B6%E4%BB%A3%E7%A0%81%E6%96%87%E4%BB%B6.html&quot;&gt;VisualStudio 合并代码文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E4%BF%AE%E6%94%B9%E9%85%8D%E8%89%B2.html&quot;&gt;VisualStudio 修改配色&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Visual-Studio-%E6%9F%A5%E7%9C%8B%E9%A1%B9%E7%9B%AE%E7%9A%84%E8%83%BD%E5%8A%9B.html&quot;&gt;Visual Studio 查看项目的能力&lt;/a&gt;
&lt;!-- [Visual Studio 查看项目的能力 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18409403 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/git-%E4%BD%BF%E7%94%A8-VisualStudio-%E6%AF%94%E8%BE%83%E5%88%86%E6%94%AF%E6%9B%B4%E6%94%B9.html&quot;&gt;git 使用 VisualStudio 比较分支更改&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-VisualStudio-2017-%E9%A1%B9%E7%9B%AE%E6%96%87%E4%BB%B6.html&quot;&gt;WPF 使用 VisualStudio 2017 项目文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E5%AE%89%E8%A3%85-Python-%E5%BC%80%E5%8F%91.html&quot;&gt;VisualStudio 安装 Python 开发&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E9%80%9A%E8%BF%87-EditorBrowsable-%E9%9A%90%E8%97%8F%E4%B8%8D%E5%BC%80%E6%94%BE%E7%9A%84%E5%B1%9E%E6%80%A7%E6%88%96%E6%96%B9%E6%B3%95.html&quot;&gt;VisualStudio 通过 EditorBrowsable 隐藏不开放的属性或方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E8%BF%87%E6%BB%A4%E8%BE%93%E5%87%BA%E7%AA%97%E5%8F%A3%E6%96%87%E6%9C%AC.html&quot;&gt;VisualStudio 过滤输出窗口文本&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E9%80%9A%E8%BF%87%E9%85%8D%E7%BD%AE-DefaultXamlRuntime-%E5%B1%9E%E6%80%A7-%E8%AE%A9%E6%8E%A7%E5%88%B6%E5%8F%B0%E9%A1%B9%E7%9B%AE%E9%87%8C%E7%9A%84-XAML-%E5%BA%94%E7%94%A8%E4%B8%8A%E6%99%BA%E8%83%BD%E6%8F%90%E7%A4%BA.html&quot;&gt;VisualStudio 通过配置 DefaultXamlRuntime 属性 让控制台项目里的 XAML 应用上智能提示&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-csproj-%E6%B7%BB%E5%8A%A0-ItemGroup-%E7%9A%84-Service.html&quot;&gt;VisualStudio csproj 添加 ItemGroup 的 Service &lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;visualstudio-调试&quot;&gt;VisualStudio 调试&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E6%96%AD%E7%82%B9%E8%B0%83%E8%AF%95%E8%AF%A6%E8%A7%A3.html&quot;&gt;VisualStudio 断点调试详解&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E8%B0%83%E8%AF%95%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F%E6%96%B9%E6%B3%95.html&quot;&gt;VisualStudio 调试内存泄漏方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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.html&quot;&gt;Roslyn 让 VisualStudio 急速调试底层库方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AD%A6%E6%83%95-Visual-Studio-%E5%B1%9E%E6%80%A7%E6%B1%82%E5%80%BC%E5%89%AF%E4%BD%9C%E7%94%A8%E5%AF%BC%E8%87%B4%E9%80%BB%E8%BE%91%E4%B8%8D%E7%AC%A6%E5%90%88%E9%A2%84%E6%9C%9F.html&quot;&gt;警惕 Visual Studio 属性求值副作用导致逻辑不符合预期&lt;/a&gt;
&lt;!-- [警惕 Visual Studio 属性求值副作用导致逻辑不符合预期 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18436921 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/UOS-%E5%BC%80%E5%90%AF-VisualStudio-%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%95-.NET-%E5%BA%94%E7%94%A8%E4%B9%8B%E6%97%85.html&quot;&gt;UOS 开启 VisualStudio 远程调试 .NET 应用之旅&lt;/a&gt;
&lt;!-- [UOS 开启 VisualStudio 远程调试 .NET 应用之旅 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18086533 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E5%A6%82%E4%BD%95-SSH-%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%95-Linux-%E7%9A%84-dotnet-%E5%BA%94%E7%94%A8%E7%9A%84%E5%90%AF%E5%8A%A8.html&quot;&gt;VisualStudio 如何 SSH 远程调试 Linux 的 dotnet 应用的启动&lt;/a&gt;
&lt;!-- [VisualStudio 如何 SSH 远程调试 Linux 的 dotnet 应用的启动 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18238164 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;p&gt;&lt;a href=&quot;/post/VisualStudio-%E5%BC%80%E5%90%AF%E4%BB%85%E6%88%91%E4%BB%A3%E7%A0%81%E8%B0%83%E8%AF%95.html&quot;&gt;VisualStudio 开启仅我代码调试&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E9%80%9A%E8%BF%87%E5%A4%96%E9%83%A8%E8%B0%83%E8%AF%95%E6%96%B9%E6%B3%95%E5%BF%AB%E9%80%9F%E8%B0%83%E8%AF%95%E5%BA%93%E4%BB%A3%E7%A0%81.html&quot;&gt;VisualStudio 通过外部调试方法快速调试库代码&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E6%89%93%E6%96%AD%E7%82%B9%E8%B0%83%E8%AF%95%E5%92%8C%E4%B8%8D%E6%89%93%E6%96%AD%E7%82%B9%E8%B0%83%E8%AF%95%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB.html&quot;&gt;VisualStudio 打断点调试和不打断点调试有什么区别&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E5%9C%A8-DebuggerDisplay-%E7%9A%84%E5%B1%9E%E6%80%A7%E6%9B%B4%E6%94%B9%E4%B8%9A%E5%8A%A1%E9%80%BB%E8%BE%91%E5%B0%86%E4%BC%9A%E8%AE%A9%E8%B0%83%E8%AF%95%E5%92%8C%E9%9D%9E%E8%B0%83%E8%AF%95%E4%B8%8B%E9%80%BB%E8%BE%91%E4%B8%8D%E5%90%8C.html&quot;&gt;VisualStudio 在 DebuggerDisplay 的属性更改业务逻辑将会让调试和非调试下逻辑不同&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-2019-%E8%B0%83%E8%AF%95%E9%A1%B9%E7%9B%AE%E4%BD%BF%E7%94%A8-Portable-PDB-%E6%8F%90%E7%A4%BA%E4%B8%8D%E6%94%AF%E6%8C%81-PDB-%E6%A0%BC%E5%BC%8F.html&quot;&gt;VisualStudio 2019 调试项目使用 Portable PDB 提示不支持 PDB 格式&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E8%A7%A3%E5%86%B3%E9%A6%96%E6%AC%A1%E8%B0%83%E8%AF%95-docker-%E7%9A%84-vs2017u5-exists,-deleting-%E5%A4%AA%E6%85%A2%E9%97%AE%E9%A2%98.html&quot;&gt;VisualStudio 解决首次调试 docker 的 vs2017u5 exists, deleting 太慢问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E8%B0%83%E8%AF%95%E6%97%B6%E4%BC%9A%E4%B8%8D%E6%96%AD%E5%88%B7%E6%96%B0-WPF-%E5%BA%94%E7%94%A8%E6%B8%B2%E6%9F%93.html&quot;&gt;VisualStudio 调试时会不断刷新 WPF 应用渲染&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/visual-Studio-%E6%97%A0%E6%B3%95%E8%B0%83%E8%AF%95-%E6%8F%90%E7%A4%BA%E7%A8%8B%E5%BA%8F%E8%B7%9F%E8%B8%AA%E5%B7%B2%E9%80%80%E5%87%BA.html&quot;&gt;visual Studio 无法调试，提示程序跟踪已退出&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-2022-%E6%89%BE%E4%B8%8D%E5%88%B0%E5%86%85%E5%AD%98-%E5%8F%8D%E6%B1%87%E7%BC%96-%E5%AF%84%E5%AD%98%E5%99%A8%E8%B0%83%E8%AF%95%E5%B7%A5%E5%85%B7.html&quot;&gt;VisualStudio 2022 找不到内存 反汇编 寄存器调试工具&lt;/a&gt; &lt;a href=&quot;https://cloud.tencent.com/developer/article/2378995&quot;&gt;腾讯云&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;visualstudio-插件收藏&quot;&gt;VisualStudio 插件收藏&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E5%A5%BD%E7%94%A8%E6%8F%92%E4%BB%B6%E9%9B%86%E5%90%88.html&quot;&gt;VisualStudio 好用插件集合&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E7%BC%96%E7%A0%81%E8%A7%84%E8%8C%83%E5%B7%A5%E5%85%B7-2.6-%E4%BF%AE%E6%94%B9%E5%BD%93%E5%89%8D%E6%96%87%E4%BB%B6%E7%BC%96%E7%A0%81.html&quot;&gt;VisualStudio 编码规范工具 2.6 修改当前文件编码&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E6%8F%92%E4%BB%B6-%E7%BF%BB%E8%AF%91%E6%B3%A8%E9%87%8A.html&quot;&gt;VisualStudio 插件 翻译注释&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;visualstudio-扩展插件开发&quot;&gt;VisualStudio 扩展插件开发&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E6%89%A9%E5%B1%95%E5%BC%80%E5%8F%91.html&quot;&gt;VisualStudio 扩展开发&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E6%89%A9%E5%B1%95%E5%BC%80%E5%8F%91-%E8%8E%B7%E5%BE%97%E8%BE%93%E5%87%BA%E7%AA%97%E5%8F%A3%E5%86%85%E5%AE%B9.html&quot;&gt;VisualStudio 扩展开发 获得输出窗口内容&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E6%89%A9%E5%B1%95%E5%BC%80%E5%8F%91-%E6%B7%BB%E5%8A%A0%E8%BE%93%E5%87%BA%E7%AA%97%E5%8F%A3.html&quot;&gt;VisualStudio 扩展开发 添加输出窗口&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E6%89%A9%E5%B1%95%E5%BC%80%E5%8F%91-%E6%B7%BB%E5%8A%A0%E8%8F%9C%E5%8D%95.html&quot;&gt;VisualStudio 扩展开发 添加菜单&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;cicd&quot;&gt;CICD&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%A6%82%E4%BD%95%E7%BB%99-CI-CD-%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%90%AD%E5%BB%BA%E4%B8%8A-.NET-5-%E6%9E%84%E5%BB%BA%E5%92%8C%E8%BF%90%E8%A1%8C%E7%8E%AF%E5%A2%83.html&quot;&gt;如何给 CI CD 服务器搭建上 .NET 5 构建和运行环境&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;github-action&quot;&gt;GitHub Action&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%83%A8%E7%BD%B2-github-%E7%9A%84-Action-%E8%BF%9B%E8%A1%8C%E6%8C%81%E7%BB%AD%E9%9B%86%E6%88%90.html&quot;&gt;dotnet 部署 github 的 Action 进行持续集成&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;dotnet 配置 github 自动打包上传 nuget 文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%85%8D%E5%90%88-GitHub-%E7%9A%84-Action-%E5%81%9A%E8%87%AA%E5%8A%A8%E6%8E%A8-Tag-%E6%97%B6%E6%89%93%E5%8C%85-NuGet-%E5%8C%85.html&quot;&gt;dotnet 配合 GitHub 的 Action 做自动推 Tag 时打包 NuGet 包&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Github-%E7%BB%99%E4%BB%93%E5%BA%93%E4%B8%8A%E4%BC%A0-NuGet-%E5%BA%93.html&quot;&gt;Github 给仓库上传 NuGet 库&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%9C%A8-GitHub-%E7%9A%84-Action-%E4%B8%8A%E9%83%A8%E7%BD%B2%E8%87%AA%E5%8A%A8%E4%BB%A3%E7%A0%81%E7%BC%96%E7%A0%81%E8%A7%84%E8%8C%83%E6%9C%BA%E5%99%A8%E4%BA%BA.html&quot;&gt;dotnet 在 GitHub 的 Action 上部署自动代码编码规范机器人&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%9C%A8-GitHub-%E4%BB%93%E5%BA%93%E6%B7%BB%E5%8A%A0-NuGet-%E7%89%88%E6%9C%AC%E5%9B%BE%E6%A0%87%E5%92%8C%E6%9E%84%E5%BB%BA%E5%9B%BE%E6%A0%87.html&quot;&gt;在 GitHub 仓库添加 NuGet 版本图标和构建图标&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/GitHub-Action-%E6%96%B0%E4%B8%8A%E7%BA%BF-WPF-.NET-Core-%E8%87%AA%E5%8A%A8%E6%9E%84%E5%BB%BA%E6%A8%A1%E6%9D%BF.html&quot;&gt;GitHub Action 新上线 WPF .NET Core 自动构建模板&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%9F%BA%E4%BA%8E-dotnet-format-%E7%9A%84-GitHub-Action-%E8%87%AA%E5%8A%A8%E4%BB%A3%E7%A0%81%E6%A0%BC%E5%BC%8F%E5%8C%96%E6%9C%BA%E5%99%A8%E4%BA%BA.html&quot;&gt;dotnet 基于 dotnet format 的 GitHub Action 自动代码格式化机器人&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/github-%E8%AE%BE%E7%BD%AE%E8%87%AA%E5%8A%A8%E5%88%A0%E9%99%A4%E5%90%88%E5%B9%B6%E7%9A%84%E5%88%86%E6%94%AF.html&quot;&gt;github 设置自动删除合并的分支&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%83%A8%E7%BD%B2-GitHub-%E7%9A%84-Action-Runner-%E5%88%B6%E4%BD%9C%E8%87%AA%E6%89%98%E7%AE%A1%E8%BF%90%E8%A1%8C%E5%99%A8.html&quot;&gt;dotnet 部署 GitHub 的 Action Runner 制作自托管运行器&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/GitHub-%E5%A6%82%E4%BD%95%E8%BF%87%E6%BB%A4%E6%9F%90%E4%B8%AA%E4%BD%9C%E8%80%85%E7%9A%84-MR-%E5%86%85%E5%AE%B9.html&quot;&gt;GitHub 如何过滤某个作者的 MR 内容&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Github-%E6%B7%BB%E5%8A%A0-Action-%E7%BC%96%E8%AF%91%E5%9B%BE%E6%A0%87.html&quot;&gt;Github 添加 Action 编译图标&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/GitHub-%E7%9A%84-Action-%E6%8E%A5%E5%85%A5-Stryker.NET-%E8%BF%9B%E8%A1%8C%E8%87%AA%E5%8A%A8%E5%8C%96%E6%B5%8B%E8%AF%95%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95%E9%B2%81%E6%A3%92%E6%80%A7.html&quot;&gt;GitHub 的 Action 接入 Stryker.NET 进行自动化测试单元测试鲁棒性&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%80%9A%E8%BF%87-GitHub-%E7%9A%84-Action-%E8%BE%85%E5%8A%A9%E4%B8%8B%E8%BD%BD%E5%9B%BD%E5%A4%96%E8%B5%84%E6%BA%90%E6%96%87%E4%BB%B6%E7%9A%84%E7%A6%BB%E7%BA%BF%E4%B8%8B%E8%BD%BD%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet 通过 GitHub 的 Action 辅助下载国外资源文件的离线下载方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/GitHub-%E7%9A%84-Action-%E5%A6%82%E4%BD%95%E7%A6%81%E7%94%A8.html&quot;&gt;GitHub 的 Action 如何禁用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/GitHub-%E7%9A%84-Action-%E5%88%A4%E6%96%AD%E4%BB%85%E5%9C%A8%E4%B8%BB%E4%BB%93%E5%BA%93%E6%89%8D%E6%89%A7%E8%A1%8C%E8%84%9A%E6%9C%AC.html&quot;&gt;GitHub 的 Action 判断仅在主仓库才执行脚本&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BF%AE%E5%A4%8D-GitHub-Action-%E6%9E%84%E5%BB%BA%E8%BF%87%E7%A8%8B%E6%8F%90%E7%A4%BA-NETSDK1127-%E9%94%99%E8%AF%AF.html&quot;&gt;dotnet 修复 GitHub Action 构建过程提示 NETSDK1127 错误&lt;/a&gt; &lt;a href=&quot;https://cloud.tencent.com/developer/article/2380232&quot;&gt;腾讯云&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E6%94%AF%E6%8C%81%E8%BE%83%E4%BD%8E-GLibC-%E7%89%88%E6%9C%AC%E7%9A%84-dotnet-AOT-GitHub-Action-%E6%9E%84%E5%BB%BA%E6%96%B9%E6%B3%95.html&quot;&gt;支持较低 GLibC 版本的 dotnet AOT GitHub Action 构建方法&lt;/a&gt;
&lt;!-- [支持较低 GLibC 版本的 dotnet AOT GitHub Action 构建方法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19055031 ) --&gt;&lt;/p&gt;

&lt;h4 id=&quot;gitlab&quot;&gt;Gitlab&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%85%8D%E7%BD%AE-Gitlab-%E7%9A%84-Runner-%E5%81%9A-CI-%E8%87%AA%E5%8A%A8%E6%9E%84%E5%BB%BA.html&quot;&gt;dotnet 配置 Gitlab 的 Runner 做 CI 自动构建&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%85%8D%E5%90%88-Gitlab-%E5%81%9A%E8%87%AA%E5%8A%A8%E6%8E%A8-Tag-%E6%97%B6%E6%89%93%E5%8C%85-NuGet-%E5%8C%85.html&quot;&gt;dotnet 配合 Gitlab 做自动推 Tag 时打包 NuGet 包&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%B0%86%E8%87%AA%E5%8A%A8%E4%BB%A3%E7%A0%81%E6%A0%BC%E5%BC%8F%E5%8C%96%E6%9C%BA%E5%99%A8%E4%BA%BA%E5%B8%A6%E5%85%A5%E5%9B%A2%E9%98%9F-GitLab-%E5%B9%B3%E5%8F%B0.html&quot;&gt;dotnet 将自动代码格式化机器人带入团队 GitLab 平台&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%A6%82%E4%BD%95%E8%AE%A9-Gitlab-%E7%9A%84-Runner-%E5%9C%A8%E6%9E%84%E5%BB%BA%E6%97%B6%E6%8B%89%E5%8F%96-Git-Submodules-%E4%BB%93%E5%BA%93.html&quot;&gt;如何让 Gitlab 的 Runner 在构建时拉取 Git Submodules 仓库&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BF%AE%E5%A4%8D-GitLab-%E7%9A%84-CI-Runner-%E6%8F%90%E7%A4%BA%E6%89%BE%E4%B8%8D%E5%88%B0-pwsh-%E6%89%A7%E8%A1%8C%E6%96%87%E4%BB%B6.html&quot;&gt;修复 GitLab 的 CI Runner 提示找不到 pwsh 执行文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/ASP.NET-Core-%E8%BF%9E%E6%8E%A5-GitLab-%E4%B8%8E-MatterMost-%E6%89%93%E9%80%A0-devops-%E5%B7%A5%E5%85%B7.html&quot;&gt;ASP.NET Core 连接 GitLab 与 MatterMost 打造 devops 工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%88%B6%E4%BD%9C%E4%B8%80%E4%B8%AA%E8%83%BD%E6%9E%84%E5%BB%BA-dotnet-AOT-%E7%9A%84-gitlab-ruuner-%E7%9A%84-Debian-docker-%E9%95%9C%E5%83%8F.html&quot;&gt;制作一个能构建 dotnet AOT 的 gitlab ruuner 的 Debian docker 镜像&lt;/a&gt;
&lt;!-- [制作一个能构建 dotnet AOT 的 gitlab runner 的 Debian docker 镜像 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18164886 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E9%85%8D%E7%BD%AE-Gitlab-%E7%9A%84-CI-%E6%89%BE%E4%B8%8D%E5%88%B0-Runner-%E6%88%96%E6%89%BE%E9%94%99%E7%9A%84%E5%8F%AF%E8%83%BD%E5%8E%9F%E5%9B%A0.html&quot;&gt;dotnet 配置 Gitlab 的 CI 找不到 Runner 或找错的可能原因&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%9C%A8-Gitlab-%E5%BC%80%E5%90%AF-MatterMost-%E6%9C%BA%E5%99%A8%E4%BA%BA.html&quot;&gt;在 Gitlab 开启 MatterMost 机器人&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-tool-%E5%88%9B%E5%BB%BA-GitLab-%E5%90%88%E5%B9%B6%E8%AF%B7%E6%B1%82-Merge-Requests-%E5%B7%A5%E5%85%B7.html&quot;&gt;dotnet tool 创建 GitLab 合并请求 Merge Requests 工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BB%8E-gitlab-%E9%85%8D%E7%BD%AE%E7%AE%A1%E7%90%86%E8%81%8A%E8%81%8A%E5%9B%A2%E9%98%9F%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86.html&quot;&gt;从 gitlab 配置管理聊聊团队项目管理&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Windows-11-%E5%9C%A8-GitLab-Runner-%E6%9C%8D%E5%8A%A1%E5%86%85%E6%89%BE%E4%B8%8D%E5%88%B0-SMB-%E6%8C%82%E8%BD%BD%E7%9A%84-NAS-%E6%96%87%E4%BB%B6%E5%A4%B9.html&quot;&gt;Windows 11 在 GitLab Runner 服务内找不到 SMB 挂载的 NAS 文件夹&lt;/a&gt;
&lt;!-- [Windows 11 在 GitLab Runner 服务内找不到 SMB 挂载的 NAS 文件夹 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18827932 ) --&gt;&lt;/p&gt;

&lt;h4 id=&quot;杂项&quot;&gt;杂项&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%BD%BF%E7%94%A8-appveyor-%E8%87%AA%E5%8A%A8%E6%9E%84%E5%BB%BA.html&quot;&gt;开源项目使用 appveyor 自动构建&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Fiddler-%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91-%E5%B0%86%E6%8F%92%E4%BB%B6%E6%94%BE%E5%9C%A8%E7%8B%AC%E7%AB%8B%E5%AD%90%E6%96%87%E4%BB%B6%E5%A4%B9%E9%98%B2%E6%AD%A2-DLL-%E5%86%B2%E7%AA%81.html&quot;&gt;Fiddler 插件开发 将插件放在独立子文件夹防止 DLL 冲突&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;nuget&quot;&gt;NuGet&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%89%93%E5%8C%85-NuGet-%E7%9A%84%E9%85%8D%E7%BD%AE%E5%B1%9E%E6%80%A7%E5%A4%A7%E5%85%A8%E6%95%B4%E7%90%86.html&quot;&gt;dotnet 打包 NuGet 的配置属性大全整理&lt;/a&gt;
&lt;!-- [dotnet 打包 NuGet 的配置属性大全整理 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18826022 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/msbuild-%E9%A1%B9%E7%9B%AE%E6%96%87%E4%BB%B6%E5%B8%B8%E7%94%A8%E5%88%A4%E6%96%AD%E6%9D%A1%E4%BB%B6.html&quot;&gt;msbuild 项目文件常用判断条件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/How-to-generate-NuGet-package-with-Git-Tag-version-using-GitHub-Action.html&quot;&gt;How to generate NuGet package with Git Tag version using GitHub Action&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E7%94%A8-ASP.NET-Core-%E5%88%B6%E4%BD%9C%E4%B8%80%E4%B8%AA%E5%8F%AF%E4%BB%A5%E4%B8%8A%E4%BC%A0%E5%BA%93%E6%96%87%E4%BB%B6%E7%9A%84-NuGet-%E6%9C%8D%E5%8A%A1%E5%99%A8.html&quot;&gt;dotnet 用 ASP.NET Core 制作一个可以上传库文件的 NuGet 服务器&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E7%94%A8-NuGet-%E5%B0%86%E8%87%AA%E5%B7%B1%E7%9A%84%E5%B7%A5%E5%85%B7%E4%BD%9C%E4%B8%BA-dotnet-tool-%E5%88%86%E5%8F%91.html&quot;&gt;dotnet 用 NuGet 将自己的工具作为 dotnet tool 分发&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-%E6%89%93%E5%8C%85-NuGet-%E5%8C%85%E6%B7%BB%E5%8A%A0%E6%94%B9%E5%8A%A8%E6%97%A5%E5%BF%97.html&quot;&gt;Roslyn 打包 NuGet 包添加改动日志&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%B8%BA%E4%BB%80%E4%B9%88%E6%AF%8F%E4%B8%AA%E9%A1%B9%E7%9B%AE%E9%83%BD%E4%BC%9A%E8%BE%93%E5%87%BA%E4%B8%80%E4%B8%AA-NuGet-%E5%8C%85%E8%80%8C%E4%B8%8D%E6%98%AF%E4%B8%80%E4%B8%AA%E5%8C%85%E5%B8%A6%E6%89%80%E6%9C%89%E9%A1%B9%E7%9B%AE.html&quot;&gt;dotnet 为什么每个项目都会输出一个 NuGet 包而不是一个包带所有项目&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-%E5%9C%A8-NuGet-%E5%8C%85%E4%B8%AD%E6%94%BE%E6%B3%A8%E9%87%8A-xml-%E6%96%87%E4%BB%B6%E7%9A%84%E6%96%B9%E6%B3%95.html&quot;&gt;Roslyn 在 NuGet 包中放注释 xml 文件的方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/NuGet-%E6%9B%B4%E6%96%B0%E5%BA%93%E6%96%B0%E7%89%88%E6%9C%AC%E7%9A%84%E7%BC%93%E5%AD%98%E9%97%AE%E9%A2%98.html&quot;&gt;NuGet 更新库新版本的缓存问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%9C%A8-NuGet-%E4%B8%8A%E6%90%9C%E5%AF%BB%E5%A5%BD%E7%94%A8%E7%9A%84-dotnet-tool-%E5%B7%A5%E5%85%B7.html&quot;&gt;dotnet 在 NuGet 上搜寻好用的 dotnet tool 工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/NuGet-%E5%A6%82%E4%BD%95%E8%AE%BE%E7%BD%AE%E5%9B%BE%E6%A0%87.html&quot;&gt;NuGet 如何设置图标&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-SourceLink-%E5%B0%86-NuGet-%E9%93%BE%E6%8E%A5%E6%BA%90%E4%BB%A3%E7%A0%81%E5%88%B0-GitHub-%E7%AD%89%E4%BB%93%E5%BA%93.html&quot;&gt;dotnet 使用 SourceLink 将 NuGet 链接源代码到 GitHub 等仓库&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/NuGet-%E5%91%BD%E4%BB%A4%E8%A1%8C%E4%B8%8A%E4%BC%A0%E6%89%BE%E4%B8%8D%E5%88%B0-snupkg-%E6%96%87%E4%BB%B6.html&quot;&gt;NuGet 命令行上传找不到 snupkg 文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-%E9%80%9A%E8%BF%87-NuGet-%E5%BA%93%E4%BF%AE%E6%94%B9%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E5%85%A5%E5%8F%A3%E5%87%BD%E6%95%B0.html&quot;&gt;Roslyn 通过 NuGet 库修改应用程序入口函数&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E4%BD%BF%E7%94%A8%E6%96%B0%E9%A1%B9%E7%9B%AE%E6%A0%BC%E5%BC%8F%E5%BF%AB%E9%80%9F%E6%89%93%E5%87%BA-Nuget-%E5%8C%85.html&quot;&gt;VisualStudio 使用新项目格式快速打出 Nuget 包&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%8E%B7%E5%8F%96-Nuget-%E7%89%88%E6%9C%AC%E5%8F%B7.html&quot;&gt;获取 Nuget 版本号&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%A6%82%E4%BD%95%E7%A7%BB%E5%8A%A8-nuget-%E7%BC%93%E5%AD%98%E6%96%87%E4%BB%B6%E5%A4%B9.html&quot;&gt;如何移动 nuget 缓存文件夹&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Github-%E7%BB%99%E4%BB%93%E5%BA%93%E4%B8%8A%E4%BC%A0-NuGet-%E5%BA%93.html&quot;&gt;Github 给仓库上传 NuGet 库&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/How-to-fix-nuget-Unrecognized-license-type-MIT-when-pack.html&quot;&gt;How to fix nuget Unrecognized license type MIT when pack&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-%E5%BC%80%E5%8F%91-NuGet-%E5%8C%85%E7%9A%84-Task-%E7%BC%96%E8%AF%91%E5%8F%AF%E8%83%BD%E9%81%87%E5%88%B0%E7%9A%84%E9%97%AE%E9%A2%98.html&quot;&gt;Roslyn 开发 NuGet 包的 Task 编译可能遇到的问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/NuGet-%E7%AC%A6%E5%8F%B7%E6%9C%8D%E5%8A%A1%E5%99%A8.html&quot;&gt;NuGet 符号服务器&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/VisualStudio-%E5%A6%82%E4%BD%95%E5%9C%A8-NuGet-%E5%8C%85%E9%87%8C%E9%9D%A2%E5%90%8C%E6%97%B6%E5%8C%85%E5%90%AB-DEBUG-%E5%92%8C-RELEASE-%E7%9A%84%E5%BA%93.html&quot;&gt;VisualStudio 如何在 NuGet 包里面同时包含 DEBUG 和 RELEASE 的库&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Nuget-%E9%80%9A%E8%BF%87-dotnet-%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%8F%91%E5%B8%83.html&quot;&gt;Nuget 通过 dotnet 命令行发布&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BF%AE%E5%A4%8D-VisualStudio-%E6%9E%84%E5%BB%BA%E6%97%B6%E6%B2%A1%E6%9C%89%E5%B0%86-NuGet-%E7%9A%84-PDB-%E7%AC%A6%E5%8F%B7%E6%96%87%E4%BB%B6%E6%8B%B7%E8%B4%9D%E5%88%B0%E8%BE%93%E5%87%BA%E6%96%87%E4%BB%B6%E5%A4%B9.html&quot;&gt;修复 VisualStudio 构建时没有将 NuGet 的 PDB 符号文件拷贝到输出文件夹&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-%E6%89%93%E5%8C%85%E8%87%AA%E5%AE%9A%E4%B9%89%E7%9A%84%E6%96%87%E4%BB%B6%E5%88%B0-NuGet-%E5%8C%85.html&quot;&gt;Roslyn 打包自定义的文件到 NuGet 包&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-%E9%80%9A%E8%BF%87-EmbedAllSources-%E5%B0%86%E6%BA%90%E4%BB%A3%E7%A0%81%E5%B5%8C%E5%85%A5%E5%88%B0-PDB-%E7%AC%A6%E5%8F%B7%E6%96%87%E4%BB%B6%E4%B8%AD%E6%96%B9%E4%BE%BF%E5%BC%80%E5%8F%91%E8%80%85%E8%B0%83%E8%AF%95.html&quot;&gt;Roslyn 通过 EmbedAllSources 将源代码嵌入到 PDB 符号文件中方便开发者调试&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-%E6%89%93%E5%8C%85-NuGet-%E5%8C%85-BuildTransitive-%E6%96%87%E4%BB%B6%E5%A4%B9%E7%94%A8%E4%BA%8E%E7%A9%BF%E9%80%8F%E4%BE%9D%E8%B5%96%E4%BC%A0%E9%80%92%E6%8B%B7%E8%B4%9D%E6%96%87%E4%BB%B6.html&quot;&gt;Roslyn 打包 NuGet 包 BuildTransitive 文件夹用于穿透依赖传递拷贝文件&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;软件安装包&quot;&gt;软件安装包&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.walterlv.com/post/getting-started-with-wix-toolset.html&quot;&gt;WiX Toolset 安装包制作入门教程（目录篇） - walterlv&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dotnet9.com/bbs/post/2021/2/how-to-make-a-professional-software-installation-package&quot;&gt;怎么做一个专业的软件安装包？ - 码坊&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dotnet9.com/bbs/post/2022/5/How-to-make-cool-software-installation-package&quot;&gt;怎么制作炫酷软件安装包 - 码界工坊&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dotnet9.com/bbs/post/2023/7/Using-Inno-Setup-to-automatically-build-installation-packages-during-VS-compilation&quot;&gt;利用Inno Setup在VS编译时自动构建安装包 - 码界工坊&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Inno-Setup-%E5%AE%89%E8%A3%85%E5%8C%85%E8%84%9A%E6%9C%AC-Run-%E7%9A%84-Flags-%E6%A0%87%E8%AE%B0.html&quot;&gt;Inno Setup 安装包脚本 Run 的 Flags 标记&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;设计和经验&quot;&gt;设计和经验&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AE%B0%E5%B0%86%E4%B8%80%E4%B8%AA%E5%A4%A7%E5%9E%8B%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%BA%94%E7%94%A8%E9%A1%B9%E7%9B%AE%E8%BF%81%E7%A7%BB%E5%88%B0-dotnet-6-%E7%9A%84%E7%BB%8F%E9%AA%8C%E5%92%8C%E5%86%B3%E7%AD%96.html&quot;&gt;记将一个大型客户端应用项目迁移到 dotnet 6 的经验和决策&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E6%B5%85%E8%B0%88-Windows-%E6%A1%8C%E9%9D%A2%E7%AB%AF%E8%A7%A6%E6%91%B8%E6%9E%B6%E6%9E%84%E6%BC%94%E8%BF%9B.html&quot;&gt;浅谈 Windows 桌面端触摸架构演进&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%88%9B%E5%BB%BACBB%E5%BF%83%E5%BE%97.html&quot;&gt;创建CBB心得&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E6%A1%86%E6%9E%B6%E8%AE%BE%E8%AE%A1%E7%9A%84%E6%83%B3%E6%B3%95.html&quot;&gt;框架设计的想法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BD%BF%E7%94%A8-IOC-%E6%8E%A7%E5%88%B6%E5%8F%8D%E8%BD%AC%E5%92%8C-DI-%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5%E7%9A%84%E6%84%8F%E4%B9%89.html&quot;&gt;使用 IOC 控制反转和 DI 依赖注入的意义&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%B8%80%E4%BA%9B%E4%BB%A3%E7%A0%81%E5%AE%A1%E6%9F%A5%E5%A5%97%E8%B7%AF.html&quot;&gt;dotnet 一些代码审查套路&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%8F%82%E6%95%B0%E4%BD%BF%E7%94%A8-json-%E6%9C%89%E5%93%AA%E4%BA%9B%E5%9D%91.html&quot;&gt;命令行参数使用 json 有哪些坑&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0-%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E6%9C%89R5G6B5%E9%A2%9C%E8%89%B2%E6%A0%BC%E5%BC%8F.html&quot;&gt;读书笔记 为什么要有R5G6B5颜色格式&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E7%A8%8B%E5%BA%8F%E7%8C%BF%E4%BF%AE%E5%85%BB-%E7%BB%99%E5%B1%9E%E6%80%A7%E4%B8%80%E4%B8%AA%E5%8D%95%E4%BD%8D.html&quot;&gt;程序猿修养 给属性一个单位&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/GitHub-%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E5%A6%82%E4%BD%95%E5%8F%82%E4%B8%8E.html&quot;&gt;GitHub 开源项目如何参与&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%81%8A%E8%81%8A-2020-%E7%9A%84-dotnet-%E5%90%84%E5%A4%A7%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%BB%93%E5%BA%93%E7%9A%84%E6%83%85%E5%86%B5.html&quot;&gt;聊聊 2020 的 dotnet 各大开源项目仓库的情况&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3-%E5%AE%9A%E4%B9%89%E8%BF%87%E6%BB%A4%E7%9A%84%E6%96%B9%E5%BC%8F%E8%A7%A3%E8%80%A6.html&quot;&gt;编程思想 定义过滤的方式解耦&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E7%94%A8-dotnet-%E5%81%9A%E5%85%A8%E6%A0%88%E5%BC%80%E5%8F%91.html&quot;&gt;从零开始用 dotnet 做全栈开发&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%BD%AF%E4%BB%B6%E8%AE%BE%E8%AE%A1-%E7%99%BD%E8%AF%9D%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5.html&quot;&gt;软件设计 白话依赖注入&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%BC%80%E6%BA%90%E5%85%AC%E5%85%B1%E7%BB%84%E4%BB%B6%E4%BB%93%E5%BA%93%E7%9A%84%E6%9B%B4%E6%96%B0%E6%97%A5%E5%BF%97%E5%BA%94%E8%AF%A5%E5%A6%82%E4%BD%95%E5%86%99.html&quot;&gt;开源公共组件仓库的更新日志应该如何写&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%B8%BA%E4%BB%80%E4%B9%88%E4%BD%BF%E7%94%A8-GUID-%E5%81%9A%E6%96%87%E4%BB%B6%E5%90%8D%E4%B8%8D%E6%98%AF%E5%A5%BD%E4%B8%BB%E6%84%8F.html&quot;&gt;为什么使用 GUID 做文件名不是好主意&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%85%B3%E4%BA%8E%E6%8A%80%E6%9C%AF%E8%A7%84%E5%88%92%E7%9A%84%E6%83%B3%E6%B3%95.html&quot;&gt;关于技术规划的想法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%90%AC%E9%BE%99%E5%8D%8E%E8%AE%B2%E5%85%AC%E5%85%B1%E7%BB%84%E4%BB%B6-CBB-%E5%BB%BA%E8%AE%BE%E7%AC%94%E8%AE%B0.html&quot;&gt;听龙华讲公共组件 CBB 建设笔记&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E6%88%91%E5%86%99%E7%9A%84%E9%80%97%E6%AF%94%E4%BB%A3%E7%A0%81.html&quot;&gt;我写的逗比代码&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%96%87%E6%A1%A3%E5%BA%94%E7%94%A8%E7%9A%84%E6%92%A4%E9%94%80%E9%87%8D%E5%81%9A%E8%AE%BE%E8%AE%A1.html&quot;&gt;dotnet 文档应用的撤销重做设计&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E6%90%AD%E5%BB%BA%E4%B8%80%E4%B8%AA%E8%87%AA%E5%8A%A8%E5%8C%96%E5%88%86%E6%9E%90-DUMP-%E5%B9%B3%E5%8F%B0.html&quot;&gt;搭建一个自动化分析 DUMP 平台&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%8F%8C%E7%BC%93%E5%AD%98%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E8%AE%BE%E8%AE%A1-%E4%B8%8B%E8%BD%BD%E5%BA%93%E7%9A%84%E6%96%87%E4%BB%B6%E5%86%99%E5%85%A5%E7%BC%93%E5%AD%98%E6%A1%86%E6%9E%B6.html&quot;&gt;dotnet 双缓存数据结构设计 下载库的文件写入缓存框架&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E9%80%9A%E8%BF%87%E5%85%AC%E5%BC%80%E7%9A%84%E8%87%AA%E5%8A%A8%E6%9E%84%E5%BB%BA%E5%8F%91%E5%B8%83%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E7%9A%84%E5%BA%93%E7%9A%84%E5%AE%89%E5%85%A8%E6%84%8F%E4%B9%89.html&quot;&gt;通过公开的自动构建发布开源项目的库的安全意义&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BD%BF%E7%94%A8-C-%E5%86%99%E8%84%9A%E6%9C%AC%E7%9A%84%E4%BC%98%E5%8A%BF%E5%92%8C%E6%96%B9%E6%B3%95.html&quot;&gt;使用 C# 写脚本的优势和方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%BC%80%E5%8F%91%E7%9A%84%E5%8D%95%E4%BB%A3%E7%A0%81%E4%BB%93%E5%BA%93%E5%92%8C%E5%A4%9A%E4%BB%A3%E7%A0%81%E4%BB%93%E5%BA%93%E7%9A%84%E4%BC%98%E5%8A%A3.html&quot;&gt;dotnet 开发的单代码仓库和多代码仓库的优劣&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E7%99%BD%E6%9D%BF%E7%B1%BB%E5%BA%94%E7%94%A8%E7%9A%84%E6%A8%A1%E5%BC%8F%E4%BA%A4%E4%BA%92%E8%AE%BE%E8%AE%A1%E6%96%B9%E6%A1%88.html&quot;&gt;白板类应用的模式交互设计方案&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E7%99%BD%E6%9D%BF%E7%B1%BB%E5%BA%94%E7%94%A8%E7%9A%84%E4%B8%9A%E5%8A%A1%E4%BA%8B%E4%BB%B6%E5%88%86%E5%8F%91%E6%A8%A1%E5%BC%8F.html&quot;&gt;白板类应用的业务事件分发模式&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%BA%94%E7%94%A8%E5%95%86%E5%BA%97%E7%9A%84%E6%88%98%E7%95%A5%E5%9C%B0%E4%BD%8D.html&quot;&gt;应用商店的战略地位&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E7%94%BB%E5%B8%83%E5%B7%A5%E5%85%B7%E6%A0%8F%E7%9A%84%E5%8F%AF%E6%89%A9%E5%B1%95%E8%AE%BE%E8%AE%A1.html&quot;&gt;WPF 画布工具栏的可扩展设计&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E8%B0%81%E6%94%B9%E4%BA%86%E6%88%91%E7%9A%84%E4%BB%A3%E7%A0%81.html&quot;&gt;C＃ 谁改了我的代码&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E8%B4%A3%E4%BB%BB%E9%93%BE.html&quot;&gt;C# 设计模式 责任链&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BB%8E%E4%BA%BA%E5%B7%A5%E6%80%9D%E7%BB%B4%E5%8A%A0%E4%B8%8A%E4%BA%8C%E9%98%B6%E7%86%B5%E7%8C%9C%E6%B5%8B%E4%B8%96%E7%95%8C%E5%AD%98%E5%9C%A8%E6%84%8F%E5%BF%97%E6%80%9D%E7%BB%B4.html&quot;&gt;从人工思维加上二阶熵猜测世界存在意志思维&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%A6%82%E4%BD%95%E5%8F%82%E4%B8%8E-.NET-%E7%9A%84%E5%BC%80%E5%8F%91%E5%92%8C%E8%AE%BE%E8%AE%A1.html&quot;&gt;如何参与 .NET 的开发和设计&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%97%A5%E5%BF%97%E4%B8%8A%E6%8A%A5%E7%9A%84-TracerId-%E5%92%8C-SessionId-%E7%9A%84%E6%84%8F%E4%B9%89.html&quot;&gt;dotnet 日志上报的 TracerId 和 SessionId 的意义&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;程序猿修养&quot;&gt;程序猿修养&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E7%A8%8B%E5%BA%8F%E7%8C%BF%E4%BF%AE%E5%85%BB-%E4%BB%8E%E5%AE%89%E8%A3%85-dotnet-%E5%BC%80%E5%A7%8B.html&quot;&gt;程序猿修养 从安装 dotnet 开始&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E7%A8%8B%E5%BA%8F%E7%8C%BF%E4%BF%AE%E5%85%BB-%E4%BD%BF%E7%94%A8-NuGet-%E5%8F%91%E5%B0%84%E5%8D%AB%E6%98%9F%E5%8F%AA%E9%9C%80%E8%A6%81%E4%B8%89%E6%AD%A5.html&quot;&gt;程序猿修养 使用 NuGet 发射卫星只需要三步&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E7%A8%8B%E5%BA%8F%E7%8C%BF%E4%BF%AE%E5%85%BB-%E6%97%A5%E5%BF%97%E5%BA%94%E8%AF%A5%E5%A6%82%E4%BD%95%E5%86%99.html&quot;&gt;程序猿修养 日志应该如何写&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;算法&quot;&gt;算法&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/post/PTA-6-2-%E5%A4%9A%E9%A1%B9%E5%BC%8F%E6%B1%82%E5%80%BC.html&quot;&gt;PTA 6-2 多项式求值&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%B8%8D%E4%BD%BF%E7%94%A8%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%8F%8D%E8%BD%AC%E6%A0%88.html&quot;&gt;不使用数据结构反转栈&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-%E6%90%9C%E7%B4%A2%E7%AE%97%E6%B3%95.html&quot;&gt;C# 搜索算法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E6%B1%82%E7%82%B9%E9%9B%86%E7%9A%84%E5%A4%96%E6%8E%A5%E7%9F%A9%E5%BD%A2.html&quot;&gt;求点集的外接矩形&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%9B%BE%E8%AE%BA%E5%8A%A0%E6%B3%95.html&quot;&gt;图论加法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%8D%B7%E7%A7%AF%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E5%85%A8%E9%9D%A2%E8%A7%A3%E6%9E%90.html&quot;&gt;卷积神经网络全面解析&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%95%B0%E6%8D%AE%E5%B7%AE%E5%BC%82%E7%AE%97%E6%B3%95-%E7%94%A8%E4%BA%8E%E5%87%8F%E5%B0%8FOTA%E5%86%85%E5%AE%B9.html&quot;&gt;二进制数据差异算法 用于减小OTA内容&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%88%86%E5%BD%A2%E5%92%8C%E5%9B%BE%E8%AE%BA%E7%BD%91%E7%BB%9C.html&quot;&gt;分形和图论网络&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E5%86%99%E4%B8%80%E4%B8%AA%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD-%E4%BB%8E%E4%B8%80%E4%B8%AA%E7%A5%9E%E7%BB%8F%E5%85%83%E5%BC%80%E5%A7%8B.html&quot;&gt;dotnet 从零开始写一个人工智能 从一个神经元开始&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E5%86%99%E4%B8%80%E4%B8%AA%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD.html&quot;&gt;dotnet 从零开始写一个人工智能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E6%89%8B%E7%AE%97%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C-BP-%E4%BC%A0%E6%92%AD%E7%AE%97%E6%B3%95.html&quot;&gt;手算神经网络 BP 传播算法&lt;/a&gt;
&lt;!-- [手算神经网络BP传播算法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19067357 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E7%94%9F%E6%88%90%E5%AF%86%E7%A0%81.html&quot;&gt;生成密码&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%9C%86%E5%92%8C%E7%BA%BF.html&quot;&gt;圆和线&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E7%94%A8%E7%8A%B6%E6%80%81%E6%9C%BA%E7%9A%84%E6%80%9D%E6%83%B3%E8%A7%A3%E6%9E%90%E5%AD%97%E7%AC%A6%E4%B8%B2.html&quot;&gt;用状态机的思想解析字符串&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%9B%BE%E8%AE%BA-Warshall-%E5%92%8CFloyd-%E7%9F%A9%E9%98%B5%E4%BC%A0%E9%80%92%E9%97%AD%E5%8C%85.html&quot;&gt;图论 Warshall 和Floyd 矩阵传递闭包&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/C-dotnet-%E4%B8%80%E4%B8%AA%E8%BF%98%E7%9C%8B%E7%9A%84%E8%BF%87%E5%8E%BB%E7%9A%84-B-%E6%A0%91%E5%AE%9E%E7%8E%B0.html&quot;&gt;C# dotnet 一个还看的过去的 B 树实现&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%B8%BA%E4%BD%95-987654321_123456789-%E7%9A%84%E5%80%BC%E6%98%AF-8.0000000729.html&quot;&gt;为何 987654321/123456789 的值是 8.0000000729&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;开源项目&quot;&gt;开源项目&lt;/h2&gt;

&lt;p&gt;我收藏的一些开源项目，请看 &lt;a href=&quot;/post/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE.html&quot;&gt;开源项目&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;开源工具&quot;&gt;开源工具&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E6%94%AF%E6%8C%81-dotnet-6-%E7%9A%84-dnSpy-%E7%A5%9E%E5%99%A8%E7%89%88%E6%9C%AC.html&quot;&gt;支持 dotnet 6 的 dnSpy 神器版本&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E6%8E%A8%E8%8D%90%E4%B8%80%E4%B8%AA%E5%89%AA%E8%B4%B4%E6%9D%BF%E5%86%85%E5%AE%B9%E6%9F%A5%E7%9C%8B%E5%B7%A5%E5%85%B7.html&quot;&gt;WPF 推荐一个剪贴板内容查看工具&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;开源库介绍&quot;&gt;开源库介绍&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%B8%BA%E5%A4%A7%E5%9E%8B%E5%BA%94%E7%94%A8%E6%8E%A5%E5%85%A5-ApplicationStartupManager-%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B%E6%A1%86%E6%9E%B6.html&quot;&gt;dotnet 为大型应用接入 ApplicationStartupManager 启动流程框架&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-ConfigureAwait.Fody-%E5%BA%93%E8%AE%BE%E7%BD%AE%E9%BB%98%E8%AE%A4%E7%9A%84-await-%E5%90%8C%E6%AD%A5%E4%B8%8A%E4%B8%8B%E6%96%87%E5%88%87%E6%8D%A2%E9%85%8D%E7%BD%AE.html&quot;&gt;dotnet 使用 ConfigureAwait.Fody 库设置默认的 await 同步上下文切换配置&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/SourceYard-%E5%88%B6%E4%BD%9C%E6%BA%90%E4%BB%A3%E7%A0%81%E5%8C%85.html&quot;&gt;SourceYard 制作源代码包&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E7%94%A8-MVC-%E7%9A%84%E6%96%B9%E5%BC%8F%E6%89%93%E5%BC%80-IPC-%E5%91%BD%E5%90%8D%E7%AE%A1%E9%81%93.html&quot;&gt;dotnet 用 MVC 的方式打开 IPC 命名管道&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%BC%80%E6%BA%90%E4%BA%8C%E7%BB%B4%E7%BB%98%E7%94%BB%E5%B0%8F%E5%B7%A5%E5%85%B7-GeometryToolDemo-%E9%A1%B9%E7%9B%AE.html&quot;&gt;WPF 开源二维绘画小工具 GeometryToolDemo 项目&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnetCampus.UITest.WPF-%E4%B8%80%E4%B8%AA%E6%94%AF%E6%8C%81%E4%B8%AD%E6%96%87%E7%94%A8%E4%BE%8B%E7%9A%84%E7%95%8C%E9%9D%A2%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95%E6%A1%86%E6%9E%B6.html&quot;&gt;dotnetCampus.UITest.WPF 一个支持中文用例的界面单元测试框架&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B0%86-SVG-%E8%BD%AC-XAML-%E7%9A%84%E5%B7%A5%E5%85%B7.html&quot;&gt;WPF 将 SVG 转 XAML 的工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E7%94%A8%E4%BA%8E%E8%BE%85%E5%8A%A9%E5%81%9A%E4%BA%8C%E5%88%86%E8%B0%83%E8%AF%95%E7%9A%84%E6%9E%84%E5%BB%BA%E6%AF%8F%E4%B8%AA-commit-%E7%9A%84%E5%B7%A5%E5%85%B7.html&quot;&gt;用于辅助做二分调试的构建每个 commit 的工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E6%8E%A8%E8%8D%90%E4%B8%80%E4%B8%AA%E4%BD%BF%E7%94%A8-HardLink-%E7%A1%AC%E9%93%BE%E6%8E%A5%E5%87%8F%E5%B0%91%E9%87%8D%E5%A4%8D%E6%96%87%E4%BB%B6%E5%8D%A0%E7%94%A8%E7%A3%81%E7%9B%98%E7%A9%BA%E9%97%B4%E7%9A%84%E5%B7%A5%E5%85%B7.html&quot;&gt;推荐一个使用 HardLink 硬链接减少重复文件占用磁盘空间的工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E9%AB%98%E6%80%A7%E8%83%BD%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E8%AF%BB%E5%86%99%E5%BA%93-dotnetCampus.Configurations-%E7%AE%80%E4%BB%8B.html&quot;&gt;dotnet C# 高性能配置文件读写库 dotnetCampus.Configurations 简介&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BD%BF%E7%94%A8-FastTunnel-%E5%81%9A%E5%86%85%E7%BD%91%E7%A9%BF%E9%80%8F%E8%BF%9C%E7%A8%8B%E8%AE%A1%E7%AE%97%E6%9C%BA%E6%A1%8C%E9%9D%A2%E6%9C%8D%E5%8A%A1%E5%99%A8.html&quot;&gt;使用 FastTunnel 做内网穿透远程计算机桌面服务器&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/FastTunnel-%E6%90%AD%E9%85%8D-nginx-%E5%B0%86%E6%9C%AC%E5%9C%B0%E5%86%85%E7%BD%91%E7%94%B5%E8%84%91-HTTP-%E6%9C%8D%E5%8A%A1%E5%85%AC%E5%BC%80%E5%88%B0%E5%AD%90%E5%9F%9F%E5%90%8D%E5%85%AC%E7%BD%91%E8%AE%BF%E9%97%AE.html&quot;&gt;FastTunnel 搭配 nginx 将本地内网电脑 HTTP 服务公开到子域名公网访问&lt;/a&gt;
&lt;!-- [FastTunnel 搭配 nginx 将本地内网电脑 HTTP 服务公开到子域名公网访问 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18924838 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%8B%8F%E5%B7%9E-%E5%BC%80%E6%BA%90%E8%87%AA%E4%B8%BB%E7%9A%84-dotnet-%E7%94%9F%E6%80%81.html&quot;&gt;苏州 开源自主的 dotnet 生态&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E9%80%82%E5%90%88%E5%85%A5%E9%97%A8%E9%98%85%E8%AF%BB%E7%9A%84%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE-SeeGit-%E5%9B%BE%E5%BD%A2%E5%8C%96-Git-%E5%8E%86%E5%8F%B2%E8%AE%B0%E5%BD%95.html&quot;&gt;WPF 适合入门阅读的开源项目 SeeGit 图形化 Git 历史记录&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%AE%A9-C-%E5%8F%AF%E4%BB%A5%E9%80%9A%E8%BF%87%E5%8A%A8%E6%80%81%E7%94%9F%E6%88%90-HLSL-%E4%BD%BF%E7%94%A8-DX12-%E7%9A%84-GPU-%E5%B9%B6%E8%A1%8C%E8%AE%A1%E7%AE%97%E5%BA%93-ComputeSharp-%E7%9A%84%E7%AE%80%E4%BB%8B.html&quot;&gt;dotnet 让 C# 可以通过动态生成 HLSL 使用 DX12 的 GPU 并行计算库 ComputeSharp 的简介&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E7%94%A8-MVC-%E7%9A%84%E6%96%B9%E5%BC%8F%E6%89%93%E5%BC%80-IPC-%E5%91%BD%E5%90%8D%E7%AE%A1%E9%81%93.html&quot;&gt;dotnet 用 MVC 的方式打开 IPC 命名管道&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%BC%80%E6%BA%90%E4%BA%8C%E7%BB%B4%E7%BB%98%E7%94%BB%E5%B0%8F%E5%B7%A5%E5%85%B7-GeometryToolDemo-%E9%A1%B9%E7%9B%AE.html&quot;&gt;WPF 开源二维绘画小工具 GeometryToolDemo 项目&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-tool-%E6%96%87%E4%BB%B6%E7%BC%96%E7%A0%81%E8%A7%84%E8%8C%83%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%B7%A5%E5%85%B7.html&quot;&gt;dotnet tool 文件编码规范命令行工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-Refasmer-%E4%BB%8E%E7%8E%B0%E6%9C%89%E7%9A%84-DLL-%E9%87%8C%E9%9D%A2%E5%AF%BC%E5%87%BA%E5%85%AC%E5%BC%80%E7%9A%84%E6%88%90%E5%91%98%E7%BB%84%E8%A3%85%E5%87%BA%E6%96%B0%E7%9A%84%E4%BB%85%E4%BD%9C%E4%B8%BA%E5%BC%95%E7%94%A8%E7%94%A8%E9%80%94%E7%9A%84%E7%A8%8B%E5%BA%8F%E9%9B%86.html&quot;&gt;dotnet 使用 Refasmer 从现有的 DLL 里面导出公开的成员组装出新的仅作为引用用途的程序集&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E7%94%A8-Microsoft.Diagnostics.Runtime-%E5%BA%93%E5%86%99%E4%BB%A3%E7%A0%81%E8%A7%A3%E6%9E%90-DUMP-%E6%96%87%E4%BB%B6.html&quot;&gt;dotnet 用 Microsoft.Diagnostics.Runtime 库写代码解析 DUMP 文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-AsyncQueue-%E5%88%9B%E5%BB%BA%E9%AB%98%E6%80%A7%E8%83%BD%E5%86%85%E5%AD%98%E7%94%9F%E4%BA%A7%E8%80%85%E6%B6%88%E8%B4%B9%E8%80%85%E9%98%9F%E5%88%97.html&quot;&gt;dotnet 使用 AsyncQueue 创建高性能内存生产者消费者队列&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-Microsoft.Recognizers.Text-%E8%B6%85%E5%BC%BA%E5%A4%A7%E7%9A%84%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%85%B3%E9%94%AE%E8%AF%8D%E6%8F%90%E5%8F%96%E5%BA%93.html&quot;&gt;dotnet Microsoft.Recognizers.Text 超强大的自然语言关键词提取库&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E7%BB%99%E4%BB%BB%E6%84%8F%E5%AF%B9%E8%B1%A1%E9%99%84%E5%8A%A0%E4%BB%BB%E6%84%8F%E5%B1%9E%E6%80%A7%E7%9A%84%E5%BA%93.html&quot;&gt;dotnet 给任意对象附加任意属性的库&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BD%BF%E7%94%A8-.NET-Core-%E5%9C%A8-MAC-%E4%B8%8B%E6%8F%90%E4%BE%9B-Excel-%E7%9A%84-Power-Query-%E5%8A%9F%E8%83%BD.html&quot;&gt;使用 .NET Core 在 MAC 下提供 Excel 的 Power Query 功能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-ClearScript-%E6%89%A7%E8%A1%8C-VBScript-%E5%92%8C-JS-%E4%BB%A3%E7%A0%81-%E6%97%A0%E9%9C%80%E6%B5%8F%E8%A7%88%E5%99%A8.html&quot;&gt;dotnet 使用 ClearScript 执行 VBScript 和 JS 代码 无需浏览器&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BD%BF%E7%94%A8-Pandoc-%E6%8A%8A-Markdown-%E8%BD%AC-Docx.html&quot;&gt;使用 Pandoc 把 Markdown 转 Docx&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-System.CommandLine-%E5%86%99%E5%91%BD%E4%BB%A4%E8%A1%8C%E7%A8%8B%E5%BA%8F.html&quot;&gt;dotnet 使用 System.CommandLine 写命令行程序&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%8E%A7%E5%88%B6%E5%8F%B0-Hangfire-%E5%90%8E%E5%8F%B0%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1.html&quot;&gt;dotnet 控制台 Hangfire 后台定时任务&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Pipelines.Sockets.Unofficial-%E4%B8%80%E4%B8%AA%E7%BA%AF%E6%89%98%E7%AE%A1%E5%AE%9E%E7%8E%B0%E5%AF%B9%E6%8E%A5-System.IO.Pipelines-%E7%9A%84-Sockets-%E5%BA%93.html&quot;&gt;Pipelines.Sockets.Unofficial 一个纯托管实现对接 System.IO.Pipelines 的 Sockets 库&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-C-%E4%BD%BF%E7%94%A8-FreeType-%E8%AF%BB%E5%8F%96%E5%92%8C%E7%BB%98%E5%88%B6%E5%AD%97%E4%BD%93.html&quot;&gt;dotnet C# 使用 FreeType 读取和绘制字体&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/SixLabors.ImageSharp-%E5%A6%82%E4%BD%95%E8%AF%BB%E5%8F%96-IDAT-%E6%A0%A1%E9%AA%8C%E5%A4%B1%E8%B4%A5%E7%9A%84-png-%E5%9B%BE%E7%89%87.html&quot;&gt;SixLabors.ImageSharp 如何读取 IDAT 校验失败的 png 图片&lt;/a&gt;
&lt;!-- [SixLabors.ImageSharp 如何读取 IDAT 校验失败的 png 图片 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18559796 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BD%BF%E7%94%A8-LatestCSharpFeatures-%E5%BA%93%E8%AE%A9%E6%97%A7%E7%89%88%E6%9C%AC-dotnet-%E6%A1%86%E6%9E%B6%E9%A1%B9%E7%9B%AE%E4%BD%BF%E7%94%A8%E6%96%B0-C-%E8%AF%AD%E6%B3%95.html&quot;&gt;使用 LatestCSharpFeatures 库让旧版本 dotnet 框架项目使用新 C# 语法&lt;/a&gt;
&lt;!-- [使用 LatestCSharpFeatures 库让旧版本 dotnet 框架项目使用新 C# 语法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18606279 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8-ICU-%E5%BA%93%E8%BF%9B%E8%A1%8C%E5%88%86%E8%AF%8D%E5%92%8C%E5%88%86%E8%A1%8C.html&quot;&gt;dotnet 简单使用 ICU 库进行分词和分行&lt;/a&gt;
&lt;!-- [dotnet 简单使用 ICU 库进行分词和分行 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18622917 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-ColorCode-%E5%81%9A%E4%BB%A3%E7%A0%81%E7%9D%80%E8%89%B2%E5%99%A8.html&quot;&gt;dotnet 使用 ColorCode 做代码着色器&lt;/a&gt;
&lt;!-- [dotnet 使用 ColorCode 做代码着色器 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18687046 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-LibGit2Sharp-%E4%BD%BF%E7%94%A8%E7%AC%94%E8%AE%B0.html&quot;&gt;dotnet LibGit2Sharp 使用笔记&lt;/a&gt;
&lt;!-- [dotnet LibGit2Sharp 使用笔记 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18700407 ) --&gt;&lt;/p&gt;

&lt;h2 id=&quot;工具&quot;&gt;工具&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E9%AB%98%E6%95%88%E7%8E%87%E5%B7%A5%E5%85%B7.html&quot;&gt;高效率工具&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;dotnet-tool&quot;&gt;dotnet tool&lt;/h3&gt;

&lt;h4 id=&quot;制作-dotnet-工具&quot;&gt;制作 dotnet 工具&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%89%8B%E5%B7%A5%E6%89%93%E4%B8%80%E4%B8%AA-dotnet-tool-%E5%8C%85.html&quot;&gt;dotnet 手工打一个 dotnet tool 包&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%88%B6%E4%BD%9C%E7%9A%84-dotnet-tool-%E8%BF%90%E8%A1%8C%E5%A4%B1%E8%B4%A5%E6%8F%90%E7%A4%BA%E4%BE%9D%E8%B5%96%E7%BC%BA%E5%A4%B1.html&quot;&gt;制作的 dotnet tool 运行失败提示依赖缺失&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-tool-%E5%AE%89%E8%A3%85%E5%A4%B1%E8%B4%A5%E5%9B%A0%E4%B8%BA%E5%AF%B9%E5%BA%94%E7%9A%84%E5%BA%93%E4%B8%8D%E4%BB%85%E5%8C%85%E5%90%AB%E5%B7%A5%E5%85%B7.html&quot;&gt;dotnet tool 安装失败因为对应的库不仅包含工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-tool-%E5%B7%A5%E5%85%B7%E5%AE%89%E8%A3%85%E6%8F%90%E7%A4%BA-Could-not-find-a-part-of-the-path-%E5%AE%89%E8%A3%85%E5%A4%B1%E8%B4%A5.html&quot;&gt;dotnet tool 工具安装提示 Could not find a part of the path 安装失败&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;推荐工具&quot;&gt;推荐工具&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%B8%80%E4%BA%9B%E5%A5%BD%E7%94%A8%E7%9A%84-dotnet-tool-%E5%B7%A5%E5%85%B7.html&quot;&gt;一些好用的 dotnet tool 工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-tool-%E6%96%87%E4%BB%B6%E7%BC%96%E7%A0%81%E8%A7%84%E8%8C%83%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%B7%A5%E5%85%B7.html&quot;&gt;dotnet tool 文件编码规范命令行工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-tool-%E8%87%AA%E5%8A%A8%E6%89%BE%E5%88%B0%E9%A1%B9%E7%9B%AE%E9%87%8C%E9%9D%A2%E9%87%8D%E5%A4%8D%E7%9A%84-NuGet-%E4%BE%9D%E8%B5%96%E9%A1%B9.html&quot;&gt;dotnet tool 自动找到项目里面重复的 NuGet 依赖项&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-tool-%E5%88%A4%E6%96%AD%E5%8D%9A%E5%AE%A2%E6%96%87%E6%A1%A3%E9%93%BE%E6%8E%A5%E6%98%AF%E5%90%A6%E5%8F%AF%E7%94%A8%E7%9A%84%E5%B7%A5%E5%85%B7.html&quot;&gt;dotnet tool 判断博客文档链接是否可用的工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%87%AA%E5%8A%A8%E8%BF%81%E7%A7%BB-VS-2017-%E4%BB%A5%E5%89%8D%E7%9A%84-csproj-%E8%BD%AC%E4%B8%BA-dotnet-core-%E7%9A%84-SDK-Style-%E9%A3%8E%E6%A0%BC%E5%B7%A5%E5%85%B7.html&quot;&gt;dotnet 自动迁移 VS 2017 以前的 csproj 转为 dotnet core 的 SDK Style 风格工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E8%8E%B7%E5%8F%96%E8%BF%9B%E7%A8%8B%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%8F%82%E6%95%B0%E7%9A%84%E5%B7%A5%E5%85%B7.html&quot;&gt;dotnet 获取进程命令行参数的工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%85%A5%E9%97%A8%E5%88%B0%E6%94%BE%E5%BC%83-%E4%BD%BF%E7%94%A8-.NET-Core-%E5%8D%B8%E8%BD%BD%E5%B7%A5%E5%85%B7.html&quot;&gt;dotnet 入门到放弃 使用 .NET Core 卸载工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-serve-%E4%B8%80%E5%8F%A5%E8%AF%9D%E5%BC%80%E5%90%AF%E6%96%87%E4%BB%B6%E6%9C%8D%E5%8A%A1%E5%99%A8-%E9%80%9A%E8%BF%87-HTTP-%E5%B0%86%E6%96%87%E4%BB%B6%E5%85%B1%E4%BA%AB%E7%BB%99%E5%85%B6%E4%BB%96%E8%AE%BE%E5%A4%87.html&quot;&gt;dotnet serve 一句话开启文件服务器 通过 HTTP 将文件共享给其他设备&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E7%94%A8-gcdump-%E8%B0%83%E8%AF%95%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E5%86%85%E5%AD%98%E5%8D%A0%E7%94%A8.html&quot;&gt;dotnet 用 gcdump 调试应用程序内存占用&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;开发调试工具&quot;&gt;开发调试工具&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%B8%80%E4%BA%9B%E5%A5%BD%E7%94%A8%E7%9A%84%E5%BC%80%E5%8F%91%E8%80%85%E5%B7%A5%E5%85%B7.html&quot;&gt;一些好用的开发者工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/MSBuild-%E8%BE%93%E5%87%BA%E6%97%A5%E5%BF%97%E5%8F%AF%E8%A7%86%E5%8C%96%E5%B7%A5%E5%85%B7-MSBuild-Structured-Log-Viewer-%E7%AE%80%E4%BB%8B.html&quot;&gt;MSBuild 输出日志可视化工具 MSBuild Structured Log Viewer 简介&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AE%A9-snoop-%E6%94%AF%E6%8C%81-.NET-Core-WPF-%E8%B0%83%E8%AF%95.html&quot;&gt;让 snoop 支持 .NET Core WPF 调试&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-Infer-%E8%87%AA%E5%8A%A8%E5%88%86%E6%9E%90%E4%BB%A3%E7%A0%81%E7%BC%BA%E9%99%B7.html&quot;&gt;dotnet 使用 Infer# 自动分析代码缺陷&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BF%AE%E5%A4%8D-SmartAssembly-%E6%B7%B7%E6%B7%86-.NET-6-%E6%97%B6%E6%8F%90%E7%A4%BA-Unable-to-load-runtime-config-file-%E5%A4%B1%E8%B4%A5.html&quot;&gt;修复 SmartAssembly 混淆 .NET 6 时提示 Unable to load runtime config file 失败&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-format-%E5%BF%BD%E7%95%A5%E7%94%9F%E6%88%90%E4%BB%A3%E7%A0%81%E7%9A%84%E6%A0%BC%E5%BC%8F%E5%8C%96.html&quot;&gt;dotnet format 忽略生成代码的格式化&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E6%94%AF%E6%8C%81-dotnet-6-%E7%9A%84-dnSpy-%E7%A5%9E%E5%99%A8%E7%89%88%E6%9C%AC.html&quot;&gt;支持 dotnet 6 的 dnSpy 神器版本&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E7%94%A8%E4%BA%8E%E8%BE%85%E5%8A%A9%E5%81%9A%E4%BA%8C%E5%88%86%E8%B0%83%E8%AF%95%E7%9A%84%E6%9E%84%E5%BB%BA%E6%AF%8F%E4%B8%AA-commit-%E7%9A%84%E5%B7%A5%E5%85%B7.html&quot;&gt;用于辅助做二分调试的构建每个 commit 的工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-OpenXML-%E8%A7%A3%E5%8E%8B%E7%BC%A9%E6%96%87%E6%A1%A3%E4%B8%BA%E6%96%87%E4%BB%B6%E5%A4%B9%E5%B7%A5%E5%85%B7.html&quot;&gt;dotnet OpenXML 解压缩文档为文件夹工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/docfx-%E5%81%9A%E4%B8%80%E4%B8%AA%E5%92%8C%E5%BE%AE%E8%BD%AF%E4%B8%80%E6%A0%B7%E7%9A%84%E6%96%87%E6%A1%A3%E5%B9%B3%E5%8F%B0.html&quot;&gt;docfx 做一个和微软一样的文档平台&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%B0%86-SVG-%E8%BD%AC-XAML-%E7%9A%84%E5%B7%A5%E5%85%B7.html&quot;&gt;WPF 将 SVG 转 XAML 的工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BD%BF%E7%94%A8-SizeBench-%E5%88%86%E6%9E%90-Exe-%E6%96%87%E4%BB%B6%E4%BD%93%E7%A7%AF.html&quot;&gt;使用 SizeBench 分析 Exe 文件体积&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;resharper&quot;&gt;Resharper&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-Resharper-%E6%9B%B4%E6%94%B9%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4%E5%90%8E%E7%94%9F%E6%88%90%E6%96%87%E4%BB%B6%E5%BC%95%E7%94%A8%E6%89%BE%E4%B8%8D%E5%88%B0%E7%9A%84%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4.html&quot;&gt;WPF 使用 Resharper 更改命名空间后生成文件引用找不到的命名空间&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/resharper-%E8%B7%B3%E8%BD%AC%E5%88%B0%E6%BA%90%E4%BB%A3%E7%A0%81.html&quot;&gt;resharper 跳转到源代码&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%A7%A3%E5%86%B3-VS-%E8%B7%B3%E8%BD%AC%E5%AE%9A%E4%B9%89%E5%92%8C-Resharper-%E9%87%8D%E5%A4%8D.html&quot;&gt;解决 VS 跳转定义和 Resharper 重复&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;p&gt;&lt;a href=&quot;/post/%E4%BD%BF%E7%94%A8-Resharper-%E5%BF%AB%E9%80%9F%E5%81%9A%E9%80%82%E9%85%8D%E5%99%A8.html&quot;&gt;使用 Resharper 快速做适配器&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/resharper-%E8%87%AA%E5%AE%9A%E4%B9%89%E4%BB%A3%E7%A0%81%E7%89%87.html&quot;&gt;resharper 自定义代码片&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Resharper-%E5%8E%BB%E6%8E%89%E6%B3%A8%E9%87%8A%E6%8B%BC%E5%86%99.html&quot;&gt;Resharper 去掉注释拼写&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Resharper-%E5%A6%82%E4%BD%95%E6%8A%8A%E7%B1%BB%E9%87%8C%E7%9A%84%E7%B1%BB%E7%A7%BB%E5%8A%A8%E5%88%B0%E5%85%B6%E4%BB%96%E6%96%87%E4%BB%B6.html&quot;&gt;Resharper 如何把类里的类移动到其他文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/ReSharper-%E6%98%BE%E7%A4%BA%E4%BD%BF%E7%94%A8%E7%9A%84%E9%A2%9C%E8%89%B2.html&quot;&gt;ReSharper 显示使用的颜色&lt;/a&gt;
&lt;!-- [ReSharper 显示使用的颜色 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18178845 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/ReSharper-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E5%9C%A8-Directory.Build.props-%E5%B1%9E%E6%80%A7%E6%B1%82%E5%80%BC%E7%BB%93%E6%9E%9C%E4%B8%8D%E8%83%BD%E5%BA%94%E7%94%A8%E5%88%B0%E9%A1%B9%E7%9B%AE%E9%87%8C.html&quot;&gt;ReSharper 已知问题 在 Directory.Build.props 属性求值结果不能应用到项目里&lt;/a&gt;
&lt;!-- [ReSharper 已知问题 在 Directory.Build.props 属性求值结果不能应用到项目里 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18921496 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;sublime-text&quot;&gt;Sublime Text&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/Sublime-Text-%E5%AE%89%E8%A3%85%E4%B8%AD%E6%96%87-%E8%8B%B1%E6%96%87%E5%AD%97%E4%BD%93.html&quot;&gt;Sublime Text 安装中文、英文字体&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Sublime-Text-%E5%A5%BD%E7%94%A8%E7%9A%84%E6%8F%92%E4%BB%B6.html&quot;&gt;Sublime Text 好用的插件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/sublime-Text-%E6%AD%A3%E5%88%99%E6%9B%BF%E6%8D%A2.html&quot;&gt;sublime Text 正则替换&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E6%B7%BB%E5%8A%A0%E5%8F%B3%E9%94%AE%E4%BD%BF%E7%94%A8-SublimeText-%E6%89%93%E5%BC%80.html&quot;&gt;添加右键使用 SublimeText 打开&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-core-%E6%B7%BB%E5%8A%A0-SublimeText-%E7%BC%96%E8%AF%91%E6%8F%92%E4%BB%B6.html&quot;&gt;dotnet core 添加 SublimeText 编译插件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%9C%A8-SublimeText-%E4%BD%BF%E7%94%A8-dotnet-%E7%BC%96%E8%AF%91-C-%E9%A1%B9%E7%9B%AE.html&quot;&gt;在 SublimeText 使用 dotnet 编译 C# 项目&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/SublimeText-%E5%BF%AB%E9%80%9F%E6%89%93%E5%BC%80%E5%BD%93%E5%89%8D%E6%96%87%E4%BB%B6%E7%9A%84%E6%96%87%E4%BB%B6%E5%A4%B9.html&quot;&gt;SublimeText 快速打开当前文件的文件夹&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/SublimeText-%E7%B2%98%E8%B4%B4%E5%9B%BE%E7%89%87%E4%BF%9D%E5%AD%98%E5%88%B0%E6%9C%AC%E5%9C%B0.html&quot;&gt;SublimeText 粘贴图片保存到本地&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/SublimeText-%E9%85%8D%E7%BD%AE%E8%B7%B3%E8%BD%AC%E5%9B%9E%E4%B8%8A%E4%B8%AA%E5%85%89%E6%A0%87%E5%9D%90%E6%A0%87.html&quot;&gt;SublimeText 配置跳转回上个光标坐标&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%85%B3%E9%97%AD-SublimeText-3.2.2-Build-3211-%E7%9A%84%E6%8B%BC%E5%86%99%E6%A3%80%E6%9F%A5.html&quot;&gt;关闭 SublimeText 3.2.2 Build 3211 的拼写检查&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;git&quot;&gt;GIT&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/git-%E9%9C%80%E8%A6%81%E7%9F%A5%E9%81%93%E7%9A%841000%E4%B8%AA%E9%97%AE%E9%A2%98.html&quot;&gt;git 需要知道的1000个问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/git-%E9%80%9A%E8%BF%87-SublimeMerge-%E5%A4%84%E7%90%86%E5%86%B2%E7%AA%81.html&quot;&gt;git 通过 SublimeMerge 处理冲突&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/git-%E4%B8%8A%E4%BC%A0%E5%BD%93%E5%89%8D%E5%88%86%E6%94%AF.html&quot;&gt;git 上传当前分支&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/git-%E7%BB%9F%E8%AE%A1%E4%B8%A4%E4%B8%AA-commit-%E4%B9%8B%E9%97%B4%E7%9B%B8%E5%B7%AE%E7%9A%84%E6%AC%A1%E6%95%B0.html&quot;&gt;git 统计两个 commit 之间相差的次数&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%9B%B4%E6%96%B0%E6%9C%AC%E5%9C%B0%E6%89%80%E6%9C%89-Git-%E4%BB%93%E5%BA%93%E7%9A%84%E5%B7%A5%E5%85%B7.html&quot;&gt;dotnet 更新本地所有 Git 仓库的工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%87%AA%E5%8A%A8%E6%9B%B4%E6%96%B0%E6%89%80%E6%9C%89-Git-%E4%BB%93%E5%BA%93.html&quot;&gt;自动更新所有 Git 仓库&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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.html&quot;&gt;如何删除错误提交的 git 大文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/git-%E5%90%88%E5%B9%B6%E4%B8%A4%E4%B8%AA%E4%BB%93%E5%BA%93.html&quot;&gt;git 合并两个仓库&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/git%E6%97%A0%E6%B3%95pull%E4%BB%93%E5%BA%93refusing-to-merge-unrelated-histories.html&quot;&gt;git无法pull仓库refusing to merge unrelated histories&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/git-%E4%BF%AE%E6%94%B9commit%E6%97%A5%E6%9C%9F%E4%B8%BA%E4%B9%8B%E5%89%8D%E7%9A%84%E6%97%A5%E6%9C%9F.html&quot;&gt;git 修改commit日期为之前的日期&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/git%E9%95%9C%E5%83%8F%E4%BB%93%E5%BA%93.html&quot;&gt;git镜像仓库&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/git-%E5%88%86%E6%94%AF%E6%94%B9%E5%90%8D.html&quot;&gt;git 分支改名&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/git-%E6%8F%90%E4%BA%A4%E6%B7%BB%E5%8A%A0-emoij-%E6%96%87%E5%AD%97.html&quot;&gt;git 提交添加 emoij 文字&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/git-push-%E9%94%99%E8%AF%AF-hook-declined.html&quot;&gt;git push 错误 hook declined &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/git-rebase-%E5%90%88%E5%B9%B6%E5%A4%9A%E4%B8%AA%E6%8F%90%E4%BA%A4.html&quot;&gt;git rebase 合并多个提交&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/git-cannot-lock-ref.html&quot;&gt;git cannot lock ref&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/git-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E5%91%BD%E4%BB%A4%E8%A1%8C%E8%B0%83%E7%94%A8-git-%E6%97%B6%E5%8F%AF%E8%83%BD%E5%AD%98%E5%9C%A8%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F%E6%8A%95%E6%AF%92%E9%97%AE%E9%A2%98.html&quot;&gt;git 已知问题 命令行调用 git 时可能存在环境变量投毒问题&lt;/a&gt;
&lt;!-- [git 已知问题 命令行调用 git 时可能存在环境变量投毒问题 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18773226 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/3%E5%88%86%E9%92%9F%E6%95%99%E4%BD%A0%E6%90%AD%E5%BB%BA-gitea-%E5%9C%A8-Centos-%E6%9C%8D%E5%8A%A1%E5%99%A8.html&quot;&gt;3分钟教你搭建 gitea 在 Centos 服务器&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;azure&quot;&gt;Azure&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/Azure-%E6%97%A0%E6%9C%8D%E5%8A%A1%E5%99%A8-Function-%E5%87%BD%E6%95%B0%E8%AE%A1%E7%AE%97%E6%9C%8D%E5%8A%A1-dotnet-core-3.1-%E5%88%9B%E5%BB%BA%E5%92%8C%E9%83%A8%E7%BD%B2%E5%85%A5%E9%97%A8.html&quot;&gt;Azure 无服务器 Function 函数计算服务 dotnet core 3.1 创建和部署入门&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Azure-%E5%87%BD%E6%95%B0%E6%9C%8D%E5%8A%A1%E9%83%A8%E7%BD%B2%E5%A4%B1%E8%B4%A5-%E5%9B%A0%E4%B8%BA%E5%87%BD%E6%95%B0%E5%BA%94%E7%94%A8%E8%AE%BE%E7%BD%AE-v3-%E4%BD%86%E4%B8%BB%E6%9C%BA%E6%98%AF-v2-%E7%89%88%E6%9C%AC.html&quot;&gt;Azure 函数服务部署失败 因为函数应用设置 v3 但主机是 v2 版本&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/WPF-%E5%9F%BA%E4%BA%8E-Azure-%E7%9A%84%E8%AE%A4%E7%9F%A5%E6%9C%8D%E5%8A%A1-%E6%83%85%E7%BB%AA%E5%88%86%E6%9E%90-%E8%AF%AD%E8%A8%80%E6%A3%80%E6%B5%8B-%E5%85%B3%E9%94%AE%E7%9F%AD%E8%AF%AD%E6%8F%90%E5%8F%96.html&quot;&gt;WPF 基于 Azure 的认知服务 情绪分析 语言检测 关键短语提取&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;docker&quot;&gt;Docker&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E6%89%A7%E8%A1%8C-docker-%E5%AE%B9%E5%99%A8-error-MSB4018-CreateAppHost-%E4%BB%BB%E5%8A%A1%E6%84%8F%E5%A4%96%E5%A4%B1%E8%B4%A5%E5%8F%AF%E8%83%BD%E5%8E%9F%E5%9B%A0.html&quot;&gt;dotnet 执行 docker 容器 error MSB4018 CreateAppHost 任务意外失败可能原因&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%9F%BA%E4%BA%8E-debian-%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA-docker-%E7%9A%84-sdk-%E9%95%9C%E5%83%8F.html&quot;&gt;dotnet 基于 debian 创建一个 docker 的 sdk 镜像&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E5%88%B6%E4%BD%9C-docker-%E6%8F%90%E7%A4%BA-Insufficient-space-in-download-directory-%E7%A3%81%E7%9B%98%E7%A9%BA%E9%97%B4%E4%B8%8D%E8%B6%B3.html&quot;&gt;dotnet 制作 docker 提示 Insufficient space in download directory 磁盘空间不足&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%9C%A8-windows-%E4%B8%8A%E8%BF%90%E8%A1%8C%E7%9A%84-podman-%E9%BB%98%E8%AE%A4%E7%9A%84%E6%8C%82%E8%BD%BD%E7%9B%B8%E5%AF%B9%E8%B7%AF%E5%BE%84%E6%98%AF%E4%BB%80%E4%B9%88.html&quot;&gt;在 windows 上运行的 podman 默认的挂载相对路径是什么&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%88%B6%E4%BD%9C%E4%B8%80%E4%B8%AA%E8%83%BD%E6%9E%84%E5%BB%BA-dotnet-AOT-%E7%9A%84-gitlab-ruuner-%E7%9A%84-Debian-docker-%E9%95%9C%E5%83%8F.html&quot;&gt;制作一个能构建 dotnet AOT 的 gitlab ruuner 的 Debian docker 镜像&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;total-commander&quot;&gt;Total Commander&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/Total-Commander-%E6%98%BE%E7%A4%BA%E6%96%87%E4%BB%B6%E5%8C%85%E5%90%AB%E6%96%87%E4%BB%B6%E5%90%8D%E6%89%A9%E5%B1%95.html&quot;&gt;Total Commander 显示文件包含文件名扩展&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Total-Commander-%E4%BD%BF%E7%94%A8-mklink-%E5%BB%BA%E7%AB%8B%E6%96%87%E4%BB%B6%E5%A4%B9%E9%93%BE%E6%8E%A5-%E5%B0%86-C-%E7%9B%98%E6%96%87%E4%BB%B6%E8%BF%81%E7%A7%BB%E5%88%B0%E5%85%B6%E4%BB%96%E7%9B%98.html&quot;&gt;Total Commander 使用 mklink 建立文件夹链接 将 C 盘文件迁移到其他盘&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;powershell&quot;&gt;PowerShell&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/PowerShell-%E9%80%9A%E8%BF%87-WMI-%E8%8E%B7%E5%8F%96%E7%B3%BB%E7%BB%9F%E6%9C%8D%E5%8A%A1.html&quot;&gt;PowerShell 通过 WMI 获取系统服务&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/PowerShell-%E9%80%9A%E8%BF%87-WMI-%E8%8E%B7%E5%8F%96%E7%B3%BB%E7%BB%9F%E5%AE%89%E8%A3%85%E8%BD%AF%E4%BB%B6.html&quot;&gt;PowerShell 通过 WMI 获取系统安装软件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/PowerShell-%E9%80%9A%E8%BF%87-WMI-%E8%8E%B7%E5%8F%96%E7%B3%BB%E7%BB%9F%E4%BF%A1%E6%81%AF.html&quot;&gt;PowerShell 通过 WMI 获取系统信息&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/PowerShell-%E4%BD%BF%E7%94%A8-WMI-%E8%8E%B7%E5%8F%96%E4%BF%A1%E6%81%AF.html&quot;&gt;PowerShell 使用 WMI 获取信息&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/PowerShell-%E9%80%9A%E8%BF%87-WMI-%E8%8E%B7%E5%8F%96%E7%B3%BB%E7%BB%9F%E5%AE%89%E8%A3%85%E7%9A%84%E9%A9%B1%E5%8A%A8.html&quot;&gt;PowerShell 通过 WMI 获取系统安装的驱动&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/PowerShell-%E9%80%9A%E8%BF%87-WMI-%E8%8E%B7%E5%8F%96%E8%AE%BE%E5%A4%87%E5%8E%82%E5%95%86.html&quot;&gt;PowerShell 通过 WMI 获取设备厂商&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/PowerShell-%E9%80%9A%E8%BF%87-WMI-%E8%8E%B7%E5%8F%96%E8%A1%A5%E4%B8%81.html&quot;&gt;PowerShell 通过 WMI 获取补丁&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/PowerShell-%E6%8B%BF%E5%88%B0%E6%98%BE%E5%8D%A1%E4%BF%A1%E6%81%AF.html&quot;&gt;PowerShell 拿到显卡信息&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/PowerShell-%E5%AE%9E%E7%8E%B0-curl-%E7%9A%84%E7%94%A8%E6%88%B7%E5%90%8D%E5%92%8C%E5%AF%86%E7%A0%81%E9%80%BB%E8%BE%91.html&quot;&gt;PowerShell 实现 curl 的用户名和密码逻辑&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-core-%E4%BD%BF%E7%94%A8-PowerShell-%E8%84%9A%E6%9C%AC.html&quot;&gt;dotnet core 使用 PowerShell 脚本&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/PowerShell-%E6%8B%BF%E5%88%B0%E6%9C%80%E8%BF%91%E7%9A%8410%E4%B8%AA%E7%B3%BB%E7%BB%9F%E6%97%A5%E5%BF%97.html&quot;&gt;PowerShell 拿到最近的10个系统日志&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;bat&quot;&gt;BAT&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/BAT-%E8%84%9A%E6%9C%AC%E5%88%A4%E6%96%AD%E5%BD%93%E5%89%8D%E7%B3%BB%E7%BB%9F%E6%98%AF-x86-%E8%BF%98%E6%98%AF-x64-%E7%B3%BB%E7%BB%9F.html&quot;&gt;BAT 脚本判断当前系统是 x86 还是 x64 系统&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E9%80%9A%E8%BF%87-cmd-%E6%89%B9%E5%A4%84%E7%90%86%E6%96%87%E4%BB%B6%E5%B0%86-16-%E8%BF%9B%E5%88%B6%E8%BD%AC-10-%E8%BF%9B%E5%88%B6%E6%95%B0%E5%AD%97.html&quot;&gt;通过 cmd 批处理文件将 16 进制转 10 进制数字&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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.html&quot;&gt;如何在 CMD 启动的软件传入带空格的路径&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/cmd-%E5%A6%82%E4%BD%95%E8%B7%A8%E9%A9%B1%E5%8A%A8%E5%99%A8%E7%A7%BB%E5%8A%A8%E6%96%87%E4%BB%B6%E5%A4%B9.html&quot;&gt;cmd 如何跨驱动器移动文件夹&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;latex&quot;&gt;Latex&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/Latex-%E8%AE%BA%E6%96%87elsevier-%E6%89%8B%E6%8A%8A%E6%89%8B%E5%A6%82%E4%BD%95%E7%94%A8Latex%E5%86%99%E8%AE%BA%E6%96%87.html&quot;&gt;Latex 论文elsevier，手把手如何用Latex写论文&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/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;p&gt;&lt;a href=&quot;/post/Latex-%E5%8E%BB%E6%8E%89%E8%A1%8C%E5%8F%B7.html&quot;&gt;Latex 去掉行号&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;希沃白板&quot;&gt;希沃白板&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%B8%8C%E6%B2%83%E7%99%BD%E6%9D%BF%E5%A6%82%E4%BD%95%E5%9C%A8%E5%85%AC%E5%BC%8F%E9%87%8C%E9%9D%A2%E8%BE%93%E5%85%A5%E7%BB%9D%E5%AF%B9%E5%80%BC%E7%AC%A6%E5%8F%B7.html&quot;&gt;希沃白板如何在公式里面输入绝对值符号&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E6%8B%BC%E9%9F%B3%E8%BE%93%E5%85%A5%E6%B3%95-%E5%BF%AB%E9%80%9F%E8%BE%93%E5%85%A5%E5%B8%A6%E9%9F%B3%E8%B0%83%E7%9A%84%E5%AD%97%E7%AC%A6.html&quot;&gt;拼音输入法 快速输入带音调的字符&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%B8%8C%E6%B2%83%E7%99%BD%E6%9D%BF%E5%A6%82%E4%BD%95%E8%A7%A3%E5%86%B3%E6%89%8B%E6%9C%BA%E7%AB%AF%E6%92%AD%E6%94%BE%E8%AF%BE%E4%BB%B6%E5%86%85%E8%A7%86%E9%A2%91%E5%87%BA%E7%8E%B0%E9%BB%91%E5%B1%8F%E9%97%AA%E9%80%80.html&quot;&gt;希沃白板如何解决手机端播放课件内视频出现黑屏闪退&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%B8%8C%E6%B2%83%E7%99%BD%E6%9D%BF%E6%96%AD%E7%BD%91%E7%8E%AF%E5%A2%83%E5%85%8D%E7%99%BB%E9%99%86%E7%BC%96%E8%BE%91%E8%AF%BE%E4%BB%B6.html&quot;&gt;希沃白板断网环境免登陆编辑课件&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;jekyll&quot;&gt;jekyll&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%9C%A8-windows-%E5%AE%89%E8%A3%85-Jekyll.html&quot;&gt;在 windows 安装 Jekyll&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/jekyll-%E5%9C%A8%E5%8D%9A%E5%AE%A2%E6%B7%BB%E5%8A%A0%E6%B5%81%E7%A8%8B%E5%9B%BE.html&quot;&gt;jekyll 在博客添加流程图&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/jekyll-%E5%A6%82%E4%BD%95%E5%8A%A0%E5%AF%86%E5%8D%9A%E5%AE%A2-%E9%98%B2%E6%AD%A2%E6%8A%93%E5%8F%96.html&quot;&gt;jekyll 如何加密博客 防止抓取&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/jekyll-%E6%B7%BB%E5%8A%A0-Valine-%E8%AF%84%E8%AE%BA.html&quot;&gt;jekyll 添加 Valine 评论&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;其他工具&quot;&gt;其他工具&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AE%A9-AE-%E8%BE%93%E5%87%BA-MPEG.html&quot;&gt;让 AE 输出 MPEG &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/MobaXterm-%E4%BD%BF%E7%94%A8%E4%BB%A3%E7%90%86.html&quot;&gt;MobaXterm 使用代理&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/matlab-%E7%94%BB%E5%9B%BE.html&quot;&gt;matlab 画图&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BD%BF%E7%94%A8-ahk-%E8%AE%A9%E6%99%AE%E9%80%9A%E9%94%AE%E7%9B%98%E5%8F%98%E4%B8%BADvorak%E9%94%AE%E7%9B%98.html&quot;&gt;使用 ahk 让普通键盘变为Dvorak键盘&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/AutoHotKey-%E7%94%A8%E6%89%93%E7%A0%81%E7%9A%84%E5%BF%AB%E6%8D%B7%E9%94%AE.html&quot;&gt;AutoHotKey 用打码的快捷键&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BD%BF%E7%94%A8-RetroShare-%E5%88%86%E4%BA%AB%E8%B5%84%E6%BA%90.html&quot;&gt;使用 RetroShare 分享资源&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8-Telegram.html&quot;&gt;如何使用 Telegram&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%A6%82%E4%BD%95%E5%AE%89%E8%A3%85-btsync.html&quot;&gt;如何安装 btsync&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%AE%89%E8%A3%85-aria2.html&quot;&gt;安装 aria2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%AE%89%E8%A3%85-pip.html&quot;&gt;安装 pip&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E9%80%9A%E8%BF%87-frp-%E5%BC%80%E5%90%AF%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%89%93%E5%BC%80%E6%9C%AC%E5%9C%B0%E7%9A%84-ZeroNet-%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%A4%96%E7%BD%91%E8%AE%BF%E9%97%AE.html&quot;&gt;通过 frp 开启服务器打开本地的 ZeroNet 服务器外网访问&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%B4%A1%E7%8C%AE%E8%87%AA%E5%B7%B1%E7%9A%84%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%90%AD%E5%BB%BAtor%E4%B8%AD%E8%BD%AC.html&quot;&gt;贡献自己的服务器搭建tor中转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E9%80%9A%E8%BF%87%E5%91%BD%E4%BB%A4%E8%A1%8C%E4%BD%BF%E7%94%A8%E5%BE%AE%E4%BF%A1.html&quot;&gt;通过命令行使用微信&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%A7%A3%E5%86%B3-BT-%E5%B7%A5%E5%85%B7%E8%BF%9E%E6%8E%A5%E6%95%B0%E8%BF%87%E5%A4%9A%E5%AF%BC%E8%87%B4%E6%97%A0%E7%BD%91%E9%80%9F%E9%97%AE%E9%A2%98.html&quot;&gt;解决 BT 工具连接数过多导致无网速问题&lt;/a&gt;
&lt;!-- [解决 BT 工具连接数过多导致无网速问题 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18813929 ) --&gt;&lt;/p&gt;

&lt;h2 id=&quot;杂项-1&quot;&gt;杂项&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E9%9D%9E%E6%8A%80%E6%9C%AF-%E5%9F%BA%E4%BA%8E-VR-%E8%99%9A%E6%8B%9F%E4%B8%96%E7%95%8C%E5%BC%80%E5%B1%95%E8%99%9A%E6%8B%9F%E8%B5%84%E4%BA%A7%E7%9A%84%E7%8E%A9%E6%B3%95.html&quot;&gt;非技术 基于 VR 虚拟世界开展虚拟资产的玩法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E9%9D%9E%E6%8A%80%E6%9C%AF-%E6%8A%80%E8%83%BD%E7%9F%A5%E8%AF%86%E5%92%8C%E8%B5%84%E8%AE%AF.html&quot;&gt;非技术 技能知识和资讯&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E9%9D%9E%E6%8A%80%E6%9C%AF-%E5%9C%A8%E7%BA%BF%E6%95%99%E8%82%B2%E4%B8%8E%E7%94%B5%E4%BF%A1%E8%A1%8C%E4%B8%9A%E7%9A%84%E5%8F%91%E5%B1%95.html&quot;&gt;非技术 在线教育与电信行业的发展&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E9%9D%9E%E6%8A%80%E6%9C%AF-%E5%81%9A%E4%BA%8B%E6%83%85%E7%9A%84%E6%80%A5%E5%92%8C%E5%BF%AB%E7%9A%84%E5%B7%AE%E5%88%AB.html&quot;&gt;非技术 做事情的急和快的差别&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E9%9D%9E%E6%8A%80%E6%9C%AF-%E7%A7%9F%E8%81%98%E4%BA%BA%E5%BD%A2%E6%9C%BA%E5%99%A8%E4%BA%BA%E7%9A%84%E6%83%B3%E6%B3%95.html&quot;&gt;非技术 租聘人形机器人的想法&lt;/a&gt;
&lt;!-- [非技术 租聘人形机器人的想法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19631308 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E9%9D%9E%E6%8A%80%E6%9C%AF-%E7%BA%B3%E7%B1%B3%E6%9C%BA%E5%99%A8%E4%BA%BA%E7%9A%84%E6%83%B3%E6%B3%95.html&quot;&gt;非技术 纳米机器人的想法&lt;/a&gt;
&lt;!-- [非技术 纳米机器人的想法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19631309 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E9%9D%9E%E6%8A%80%E6%9C%AF-%E5%A4%A9%E5%AE%AB%E8%AE%A1%E5%88%92-%E6%8F%90%E9%AB%98%E7%BB%8F%E6%B5%8E.html&quot;&gt;非技术 天宫计划 提高经济&lt;/a&gt;
&lt;!-- [非技术 天宫计划 提高经济 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19631310 ) --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E9%80%97%E6%AF%94%E9%9D%A2%E8%AF%95%E5%AE%98%E6%88%90%E9%95%BF%E8%B7%AF%E7%BA%BF-%E5%A6%82%E4%BD%95%E8%AE%A9%E8%A2%AB%E9%9D%A2%E8%AF%95%E8%80%85%E8%A7%89%E5%BE%97%E7%B3%9F%E5%BF%83.html&quot;&gt;逗比面试官成长路线 如何让被面试者觉得糟心&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AE%B2%E5%B8%88%E5%9F%B9%E8%AE%AD-%E5%A4%9A%E7%B1%B3%E8%AF%BA%E5%8E%9F%E5%88%99.html&quot;&gt;讲师培训 多米诺原则&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E7%A8%8B%E5%BA%8F%E5%91%98%E7%AC%91%E8%AF%9D.html&quot;&gt;程序员笑话&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E7%A8%8B%E5%BA%8F%E5%91%98%E5%A3%81%E7%BA%B8.html&quot;&gt;程序员壁纸&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%BE%AE%E8%BD%AF%E6%8A%80%E6%9C%AF%E6%94%AF%E6%8C%81%E8%81%94%E7%B3%BB%E6%96%B9%E5%BC%8F.html&quot;&gt;微软技术支持联系方式&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%BE%AE%E8%BD%AF%E6%9C%80%E5%85%B7%E4%BB%B7%E5%80%BC%E4%B8%93%E5%AE%B6-MVP-%E5%A6%82%E4%BD%95%E8%8E%B7%E5%BE%97-Resharper-%E7%9A%84%E5%85%8D%E8%B4%B9%E5%8A%9F%E8%83%BD.html&quot;&gt;微软最具价值专家 MVP 如何获得 Resharper 的免费功能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E7%94%A8-sim-%E5%8D%A1%E5%8A%A0%E5%AF%86%E4%BF%9D%E6%8A%A4%E8%B5%84%E9%87%91.html&quot;&gt;用 sim 卡加密保护资金&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%AE%89%E8%A3%85-wordpress-%E5%87%BA%E7%8E%B0-%E6%8A%B1%E6%AD%89-%E6%88%91%E4%B8%8D%E8%83%BD%E5%86%99%E5%85%A5wp-config.php%E6%96%87%E4%BB%B6.html&quot;&gt;安装 wordpress 出现 抱歉，我不能写入wp-config.php文件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%A6%82%E4%BD%95%E5%86%99%E6%AF%95%E4%B8%9A%E8%AE%BA%E6%96%87-%E8%A1%A8%E6%A0%BC.html&quot;&gt;如何写毕业论文 表格&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%B8%80%E7%B2%92%E5%9C%A8%E6%97%B6%E7%A9%BA%E8%BD%AE%E5%9B%9E%E7%9A%84%E7%B2%92%E5%AD%90%E4%BC%9A%E5%8F%91%E7%94%9F%E4%BB%80%E4%B9%88.html&quot;&gt;一粒在时空轮回的粒子会发生什么&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%B8%80%E4%B8%AA%E5%A5%BD%E7%9A%84%E7%A8%8B%E5%BA%8F%E5%91%98.html&quot;&gt;一个好的程序员&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E7%A0%B4%E8%A7%A3360doc%E5%A4%8D%E5%88%B6.html&quot;&gt;破解360doc复制&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BA%91%E4%B9%8B%E5%B9%BB-UWP-%E8%A7%86%E9%A2%91%E6%95%99%E7%A8%8B.html&quot;&gt;云之幻 UWP 视频教程&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%BE%AE%E8%BD%AF%E6%A1%86%E6%9E%B6%E4%B8%8B%E8%BD%BD.html&quot;&gt;微软框架下载&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E7%A1%AC%E4%BB%B6%E5%88%86%E9%85%8D.html&quot;&gt;硬件分配&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%87%AA%E5%8A%A8%E6%9C%BA.html&quot;&gt;自动机&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E7%94%A8%E8%87%AA%E5%8A%A8%E6%9C%BA%E7%9A%84%E6%80%9D%E6%83%B3%E8%AF%B4%E6%98%8E%E5%85%89%E9%80%9F.html&quot;&gt;用自动机的思想说明光速&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E6%96%87%E4%BB%B6%E4%BC%A0%E8%BE%93.html&quot;&gt;文件传输&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%A4%A7%E6%96%87%E4%BB%B6%E7%9A%84%E5%AD%98%E5%82%A8%E5%92%8C%E5%A4%87%E4%BB%BD.html&quot;&gt;大文件的存储和备份&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BB%A3%E7%A0%81%E6%AE%B5.html&quot;&gt;代码段&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%BA%8C%E9%98%B6%E7%86%B5.html&quot;&gt;二阶熵&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/2019-%E5%BE%B7%E7%86%99-%E8%85%BE%E8%AE%AF%E4%BA%91-%E4%BA%91%E7%A4%BE%E5%8C%BA%E5%B9%B4%E5%BA%A6%E6%9C%80%E4%BD%B3%E4%BD%9C%E8%80%85%E5%A5%96.html&quot;&gt;2019 德熙 腾讯云 云社区年度最佳作者奖&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%B8%8A%E6%B5%B7%E5%B1%95%E7%9B%9F%E7%BD%91%E7%BB%9C%E7%A7%91%E6%8A%80%E6%9C%89%E9%99%90%E5%85%AC%E5%8F%B8%E7%9A%84-gamebox-%E7%BB%84%E4%BB%B6%E6%B3%A8%E5%85%A5%E8%BF%9B%E7%A8%8B%E5%AF%BC%E8%87%B4%E8%BD%AF%E4%BB%B6%E5%B4%A9%E6%BA%83.html&quot;&gt;上海展盟网络科技有限公司的 gamebox 组件注入进程导致软件崩溃&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%AE%B0%E8%81%94%E8%BD%AF-UniAccess-%E5%AF%BC%E8%87%B4-NSIS-%E5%AE%89%E8%A3%85%E5%8C%85%E5%90%AF%E5%8A%A8%E8%BF%9B%E7%A8%8B%E5%A4%B1%E6%95%88.html&quot;&gt;记联软 UniAccess 导致 NSIS 安装包启动进程失效&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E4%B8%AD%E5%9B%BD%E6%9C%80%E6%96%B0%E6%89%8B%E6%9C%BA%E5%8F%B7%E6%AE%B5%E5%88%92%E5%88%86%E6%83%85%E5%86%B5.html&quot;&gt;中国最新手机号段划分情况&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E7%94%A8%E8%87%AA%E5%8A%A8%E6%9C%BA%E7%9A%84%E6%80%9D%E6%83%B3%E8%AF%B4%E6%98%8E%E6%97%B6%E9%97%B4%E6%96%AD%E7%89%87%E6%98%AF%E6%97%A0%E6%B3%95%E6%84%9F%E7%9F%A5%E7%9A%84.html&quot;&gt;用自动机的思想说明时间断片是无法感知的&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/dotnet-%E4%B8%96%E7%95%8C%E7%8C%9C%E6%B5%8B-%E9%9A%8F%E6%9C%BA%E6%95%B0%E7%9A%84%E5%B0%8F%E6%B5%8B%E8%AF%95.html&quot;&gt;dotnet 世界猜测 随机数的小测试&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;p2p-下载&quot;&gt;P2P 下载&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%BE%AE%E8%BD%AF%E7%9A%84-P2P-%E4%B8%8B%E8%BD%BD%E6%96%B9%E5%BC%8F.html&quot;&gt;微软的 P2P 下载方式&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%AE%8C%E6%95%B4%E7%9A%84-P2P-%E5%BA%94%E7%94%A8%E9%9C%80%E8%A6%81%E5%8C%85%E5%90%AB%E5%93%AA%E4%BA%9B%E5%8A%9F%E8%83%BD.html&quot;&gt;完整的 P2P 应用需要包含哪些功能&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E5%9C%A8-P2P-%E6%96%87%E4%BB%B6%E5%88%86%E4%BA%AB%E5%BA%94%E7%94%A8%E4%BB%A5%E6%96%87%E4%BB%B6%E6%88%96%E6%96%87%E4%BB%B6%E6%AE%B5%E4%B8%BA%E5%8D%95%E4%BD%8D%E7%9A%84%E4%BC%98%E7%BC%BA.html&quot;&gt;在 P2P 文件分享应用以文件或文件段为单位的优缺&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;考古&quot;&gt;考古&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://hadibrais.wordpress.com/2020/04/02/the-intel-x86-microarchitectures-map/&quot;&gt;英特尔 x86 微架构图考古贴 The Intel x86 Microarchitectures Map - Micromysteries&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/%E8%80%83%E5%8F%A4-ISO-639-%E6%A0%87%E5%87%86-1988-%E5%92%8C-1989-%E7%89%88%E7%9A%84%E5%8F%98%E6%9B%B4.html&quot;&gt;考古 ISO 639 标准 1988 和 1989 版的变更&lt;/a&gt;
&lt;!-- [考古 ISO 639 标准 1988 和 1989 版的变更 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18869180 ) --&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 07 Apr 2026 23:02:12 +0000</pubDate>
        <link>/post/%E5%8D%9A%E5%AE%A2%E5%AF%BC%E8%88%AA.html</link>
        <guid isPermaLink="true">/post/%E5%8D%9A%E5%AE%A2%E5%AF%BC%E8%88%AA.html</guid>
        
        
      </item>
    
      <item>
        <title>手算神经网络 BP 传播算法</title>
        <description>&lt;p&gt;虽然说是手算，但是我还是会写一点 C# 代码，避免敲坏了计算器。我和大家保证，整个手算过程中，最终的计算结果只需要用到初高中知识。推导过程会用到部分高数的知识。我尽量将用到的知识点全列举出来，本文对学渣友好，期望能够拿出纸笔和 VisualStudio 的伙伴阅读完本文能够真的理解神经网络 BP 传播算法是如何计算的&lt;/p&gt;

&lt;!--more--&gt;

&lt;!-- CreateTime:2025/10/19 17:02:14 --&gt;

&lt;!-- 发布 --&gt;
&lt;!-- 置顶1 --&gt;

&lt;p&gt;看了一下时间，今年确实 2025 年，而不是 2015 年。在 2025 时还在聊 BP 算法实在有点一言难尽。我在 10 多年前尝试写过贴近的程序，当时写的时候有一些概念没有理解，但代码是写了也能跑，甚至于在当时世界上最快的超级计算机跑了我的代码。但不能理解的部分就是不能理解。最近在散步的时候，我的伙伴 &lt;a href=&quot;https://github.com/SeWZC&quot;&gt;https://github.com/SeWZC&lt;/a&gt; 和我说明白了求偏导的数学意义。我当初高数学了3年，别人都是只学一年（因为我不断挂科），那会以为偏导没有什么，于是缺了一个知识点，导致我对 BP 的部分理解错误。尽管代码能跑，也能符合预期进行训练，但里面关于传播算法中，如何计算各个权重参数的过程我是不能全说明白的。我最近理解了偏导的数学意义之后，再到知乎上阅读了 《通俗理解神经网络BP传播算法》(&lt;a href=&quot;https://zhuanlan.zhihu.com/p/24801814&quot;&gt;https://zhuanlan.zhihu.com/p/24801814&lt;/a&gt; ) 文章，尝试按照知乎文章的给定的内容和方法，自己手算了一遍，我就完全理解了之前我所写的代码了。担心本金鱼会忘了之前的想法，或者担心下次和伙伴聊天的时候又说错了，我就编写了本文。可以认为本文没有给出比 《通俗理解神经网络BP传播算法》(&lt;a href=&quot;https://zhuanlan.zhihu.com/p/24801814&quot;&gt;https://zhuanlan.zhihu.com/p/24801814&lt;/a&gt; ) 文章更多的内容，只是按照我自己的方式一步步推导和计算。阅读完本文，预期大家能够对神经网络有了更明了的理解。如果大家还是在迷迷糊糊地训练人工智能做玄学的活，那阅读本文可以让大家能够稍微有点落地的感觉，至少大概知道简单的 BP 神经网络是如何工作起来的&lt;/p&gt;

&lt;p&gt;我努力让大家尽量少知识地开局&lt;/p&gt;

&lt;p&gt;开始之前，期望大家已经听说过一些神经网络的概念。这里只要求听说过，不解其中的内容也没关系，有部分概念我没提到的，也许大家看完了自然就能明白，或阅读完本文之后再继续在网上搜关键词继续了解。期望听说过的概念有：神经网络、图论、矩阵、激活函数、损失函数、权重&lt;/p&gt;

&lt;p&gt;神经网络可以理解图论上的一张图，尽管在几乎所有的科普文章中，都会使用到矩阵，但从原理上说都是图论的一张图。经费有限，有了节省矩阵的出场费，本文就不带入矩阵的内容了，直接平铺进行计算。本文也不会用到多少图论的知识，只是用了一点图论上的概念。本文介绍的手算的神经网络是一个简单的有向图，只有5个点。即使没有了解过图论的伙伴，也许通过示意图也能够看懂&lt;/p&gt;

&lt;p&gt;本文的手算内容是假定咱就是一个无情的计算机，正在被训练的人工智能。咱的需要计算出图上（图论的图）的各个点的权重。为了让人类更方便理解，以下图中，我给定了明确的数值。假定输入是两个数字，期望经过咱训练出来的神经网络之后，能够获取符合预期的输出数字。这样的模拟情况，就能够覆盖非常多的情况了。也许有伙伴此时还会有一些疑惑，没关系，先带着疑惑看下去吧，我也不能一口气全交代清楚情况&lt;/p&gt;

&lt;p&gt;以下是咱的神经网络图（图论的图）的示意图&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法0.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-202510171950507944.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;其中 a 和 b 两个点负责输入，e点负责输出。各个点之间的连接存在权重，其示意图如下，下图的 wxx 表示的就是各个边的权重。写成 wxx 的形式其实就是在隐含矩阵的概念了，只是咱本文这里不需要请出矩阵而已，如果真套上矩阵的话，这里的权重就是 w行列的表示法，即 w21 就是表示第2行第1列的值。这也是一个习惯表示方法&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法1.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-202510171951145807.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;假定有输入源 x0=0.35 x1=0.9 , 期望的输出是 $y_{out}=0.5$。这就是一组训练值，当然了，在实际的神经网络训练里面，肯定会给许多组训练值的。只是现在换成人类手算，需要降低难度，就只要求训练这一组数据。假定没有神经网络，就是一个正常人类在拿到 x0=0.35 x1=0.9的输入，要求写出一个函数，让这个函数能够根据此输入计算出 $y_{out}=0.5$ 的值，估计也没那么简单（禁止直接写 $f(x0,x1)=0.5$ 这样的拖出去打靶的函数）&lt;/p&gt;

&lt;p&gt;为了更加方便大家理解，这里先给各个权重添加随机的初始值。初始值很多时候真的是随机给的，依靠的是训练过程中不断算法修改参数，从而训练出一个可用的神经网络。咱现在这个神经网络只是期望训练出当拿到 x0=0.35 x1=0.9 的输入时，能够返回 $y_{out}=0.5$ 的输出。标记了添加了随机权重初始值的示意图如下&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法2.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-202510171951328893.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831205138165-259742447.png) --&gt;&lt;/p&gt;

&lt;p&gt;好的，勤奋的伙伴可以开始拿出纸笔，在纸上画出以上示意图内容了。云程序猿们没有纸笔那就进入幻想模式，我不会让大家不至于说没纸笔就看不下去的
对于一个正常的神经网络来说，每个点就是一个神经元，神经元里面包含了激活函数。在本文这里选定的 $f(x)$ 激活函数的定义如下&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法3.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-202510171951471695.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831205213938-1628137917.png) --&gt;&lt;/p&gt;

&lt;p&gt;世界上的激活函数有许许多多，没有说一定要用哪个激活函数，大家也可以在阅读完本文之后，自己尝试其他的激活函数&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法4.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-20251017195202498.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831205223025-1028288318.png) --&gt;&lt;/p&gt;

&lt;p&gt;在本文的 BP 神经网络里面，是取所有的输入乘以各自的权重之和，进入激活函数计算，从而得到输出。我将取所有的输入乘以各自的权重之和记为 $z_n$，将经过激活函数之后的输出记为 $y_n$ ，其示意图如下&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法5.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-202510171952203951.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831205249212-376890511.png) --&gt;&lt;/p&gt;

&lt;p&gt;也许大家看到这里开始有疑惑了，凭什么神经网络是这样子的。没为什么，最简单的神经网络只需要一个神经元即可，但一个神经元能做的事情就太弱了。虽然本文的图（图论的图）有5个点，但实际上 a 和 b 点都是在接收输入，实际参与计算只有三个点，相对来说已经足够简化了。在阅读完本文之后，大家也可以试试玩玩更多点和更多层的神经网络，只不过这时候就需要多写写代码啦，全靠手算可有点难哦
似乎现在这个示意图看起来就有些复杂了，但相信如果是一步步看下来的伙伴，自然能够很快理解示意图的内容了&lt;/p&gt;

&lt;p&gt;写到这里，咱距离整个简单 BP 神经网络就只剩下 损失函数（Loss Function）的定义了。简单说明损失函数就是用来度量神经网络输出的值与预期期望值的差异程度的函数&lt;/p&gt;

&lt;p&gt;损失函数要能反馈结果和预期的距离，且还要能够求导有意义。一听到反馈结果和预期的距离，大家可能想到的就是直接减就可以了，如这样的一个函数$C=y_2-y_{out}$，但在后续步骤里一求导就约分没了，算出来的$C^{‘}=1$，自然就无法继续进行下去了。那求差的平方呢，如$C=(y_2-y_{out})^2$呢？这个就好多了，加上平方也不用去烦恼绝对值的问题了，那自然就求结果和预期的差的平方好了。试试看求导的结果 $C^{‘}=2y_2-2y_{out}$，这一求导发现约分出了2的值，那索性继续乘以二分之一好了，这样求导返回结果刚好就是简单的减法，计算机看了非常开森。于是决定出来的损失函数定义如下&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法6.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-202510171952386475.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831205442483-872868840.png) --&gt;&lt;/p&gt;

&lt;p&gt;看到这里相信大家也感受到了照现在的人类的数学能力还不能让大家随意写一个函数，就能获取预期的结果。在本文的手算过程中，也许大家也就能够理解神经网络的能力边界。所使用的各个函数都有一些原因，不一定是期望获取咋样的结果就能用对应的函数，还需要所使用的函数刚好从数学上能够帮助后续的计算&lt;/p&gt;

&lt;p&gt;如此咱的所有内容就都定义完成了。为了防止大家忘记现在的状态，我再次放入当前的示意图，咱接下来就按照这个意义图开始将自己当成一个神经网络，进行手算过程&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法7.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-202510171952528509.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831205515846-1477207870.png) --&gt;&lt;/p&gt;

&lt;p&gt;拿出纸笔的伙伴可以将上述的示意图抄在草稿纸上，咱现在就来开始第一轮的计算过程。打开了 VisualStudio 的伙伴，还请创建好控制台项目，开始记录一些代码，先是一些初始化的变量或常量的值。为了确保让大家能够校验自己的计算结果内容，我这里的代码就写固定值，而不是使用随机数，这样也好方便纸笔手算的伙伴对比计算差异&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.35&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;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x1&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;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_out&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;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;n&quot;&gt;x0&lt;/span&gt;&lt;span class=&quot;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;n&quot;&gt;x1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w11&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.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;w12&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.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;w21&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w22&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w31&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.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;w32&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;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;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;看到以上的代码，也许有伙伴有疑惑，以上代码中的 y_out 是不是不符合 C# 代码规范？我这里是借用 Latex 的表示，表示 y_out 常量，按照 Latex 写法就是 y_out 啦。因此就不要将其改成 yOut 或 YOut 哦&lt;/p&gt;

&lt;p&gt;来进行一步步的计算过程，求 $z_0$ 的值&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法8.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-202510171953107711.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831205605690-1091017835.png) --&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;z0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w11&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;w12&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;这个过程看起来十分简单，就是将所有进入 c 点的输入乘以对应的权重返回的和。同理继续计算d点的$z_1$的值&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法9.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-202510171953247033.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831205633463-398825803.png) --&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;z1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w21&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;w22&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;分别让 $z_0$ 和 $z_1$ 经过 $f(x)$ 激活函数，可分别得到 $y_0$ 和 $y_1$ 的值&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法10.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-202510171953389943.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831205718595-2052365659.png) --&gt;&lt;/p&gt;

&lt;p&gt;也许大家用计算器算出来的小数点精度有些差异，但只要前面几位差不多就可以，对于神经网络来说不用太精确
对应的代码如下，封装了 F 函数&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;double&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;kt&quot;&gt;double&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;k&quot;&gt;return&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;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;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;Math&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;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;y0&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;n&quot;&gt;z0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y1&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;n&quot;&gt;z1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;继续计算 $z_2$ 的值&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法12.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-20251017195427669.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831205748786-230847243.png) --&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;z2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w31&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w32&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;让 $z_2$ 经过激活函数，可得到$y_2$的值&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法11.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-20251017195463652.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831205814081-1327939860.png) --&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;y2&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;n&quot;&gt;z2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;此时可见这一轮的输出$y_2$距离预期的$y_{out}$还有点距离。具体度量的距离有多大，那就要经过损失函数计算看&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法13.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-202510171954445237.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831205858782-1658724592.png) --&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;c&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;n&quot;&gt;y2&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;double&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;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt;&lt;span class=&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;1.0&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;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;y2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_out&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;完成了第一轮计算，接下来就是进行第一轮训练，进行 BP 的传播算法。此时在推导计算过程，就需要用到偏导的知识了。其中包括了偏导的链式计算和对函数求导的知识。借用从 https://www.cnblogs.com/awei040519/articles/18529084 博客的一张图来说明偏导在这里的意义。在这里的意义就是假定输入到输出中间经过的神经网络的某些权重变量计算之后，能够获取预期的输出内容，求解神经网络中的权重变量的值。这个求解过程有解，即是求让损失函数快速到达一个极小点。在偏导意义里面，极大和极小这些极值是相同的，从极大到极小的转换只需乘以负一即可&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法14.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-202510171954597975.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831205922572-2078183748.png) --&gt;&lt;/p&gt;

&lt;p&gt;没有经过损失函数可不可以？可以的，但是存在的问题是，如果不用随机数随便乱碰，那怎么能够知道各个权重应该如何调，应该是朝着加号方向调还是减号方向调好呢。假定咱有无穷的计算时间，那就不停各个参数加0.00001或0或-0.00001给他全遍历即可，这样自然能够获取很（不敢说最哈）优解。但大家肯定也看出来问题，这样的做法需要大量的时间，且随着图（图论的图）上的点越多，这个计算时间就会越长，且是排列组合的快速加长，也许此时只有量子计算机才能计算了。聪明的数学家想到了利用数学上的偏导工具，协助更快速求较优解的权重数值解。如上图所示，利用偏导数协助求某一个切面上的函数导数的极值方向从而让权重的修改能够更快到达较优解，如从 https://zhuanlan.zhihu.com/p/1945144971243000845 拷贝的动图所示&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法15.gif) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-%25E6%2589%258B%25E7%25AE%2597%25E7%25A5%259E%25E7%25BB%258F%25E7%25BD%2591%25E7%25BB%259C%2520BP%2520%25E4%25BC%25A0%25E6%2592%25AD%25E7%25AE%2597%25E6%25B3%259515.gif&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210001600-280754748.gif) --&gt;&lt;/p&gt;

&lt;p&gt;读过高数的伙伴也许有疑惑，由于偏导只是求一个切面方向上的，而整个网络是依靠多个权重一起工作的，那是否会导致画出来的函数图实际上有多个极值呢？没错，这就是局部最优解陷阱。好在本文这里足够简单，还不会踩到这个坑。进入局部最优解陷阱时，也许经过了很多轮的训练，其输出也不能达到预期，这时候就是大佬们常说的炼丹结果是废丹了，需要重新修改权重为随机数重新炼丹&lt;/p&gt;

&lt;p&gt;求出来各个权重对应偏导数值之后，就可以更新各个权重的值，进行下一轮计算。下一轮计算结果输出之后，再经过损失函数判断是否结果已经接近了。如果不接近，则再次求各个权重对应偏导数值，用各个权重对应偏导数值更新各个权重的值，再进行下一轮计算。这样的过程就是简化后的 BP 神经网络训练的过程。其计算方式如下，以下列举的公式中带“’”的部分表示更新之后的权重的值&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法15.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-202510171957134063.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210115815-471952519.png) --&gt;&lt;/p&gt;

&lt;p&gt;通过以上公式不难看出，其中的核心点就是在于如何求偏导。只要求出偏导了，那自然就有了更新权重的数值的方法了&lt;/p&gt;

&lt;p&gt;开始求偏导准备来修改权重参数。开始之前先简单介绍链式法则的计算方式。在本文用到的偏导知识部分非常少，本文也只介绍本文需要用到的部分知识。假定有一个参数 x 计算出了 y 的过程中，经过了两个步骤，先经过了 g 函数，求出了 t 结果。再将 t 结果传入 f 函数，从而计算出 y 结果。此时求x的偏导则可用以下的公式&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法16.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-202510171957259389.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210145346-541602738.png) --&gt;&lt;/p&gt;

&lt;p&gt;可以看到，此时将计算拆分为两步，先求t的偏导，再求x的偏导。如此过程恰好就可以用在神经网络上，逐层逐个求偏导，且刚好可以实现累加的过程。当然，本文为了简单起见，不会减少计算过程，没有将计算过的值缓存起来省略重新计算的过程，但相信大家看到后面一眼就看出来重复部分很简单就可以省略重复计算&lt;/p&gt;

&lt;p&gt;如求w31的偏导，就可以从y2到z2再到w31这样一步步求偏导做乘法，如以下示意图所示&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法17.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-202510171957399736.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210200590-1814656446.png) --&gt;&lt;/p&gt;

&lt;p&gt;如此即可得出以下公式&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法18.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-202510171957524361.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210211127-1860273126.png) --&gt;&lt;/p&gt;

&lt;p&gt;这个过程里面，各个步骤都是一个简单的确定的函数，求偏导过程也就很简单了。不需要说整个一路将 w31带入到最终计算式里面。大家如果好奇真的一路将w31带入表达式里面，最终损失函数C有多长，且求导稍微困难。通过链式的方式可以进行一级级的计算，整体复杂度也能够降低&lt;/p&gt;

&lt;p&gt;接下来咱来开始一步步求这个偏导的内容&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法19.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-20251017195866803.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210228529-586913184.png) --&gt;&lt;/p&gt;

&lt;p&gt;相信大家的求导知识还没忘记，求导数学意义上就是求速率。如对于常量函数，如 y=5 的函数，其速率就是平的，如下图所示，自然求出来的导数就是 0 的值&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法20.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-202510171958189594.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210236641-1105601596.png) --&gt;&lt;/p&gt;

&lt;p&gt;求偏导的话，只要对求偏导的变量当成变量，其他当成常量计算。这也就是为什么求出来的式子里面 $1/2 y_{out}^2$ 会是0的值的原因。那对于一次函数呢，如y=2x 函数呢，其导数反映的速率就刚好是变量前面的系数了。如下图所示&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法21.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-202510171958369694.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210349555-1439707858.png) --&gt;&lt;/p&gt;

&lt;p&gt;于是就有&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法22.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-202510171958499324.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210401548-315861139.png) --&gt;&lt;/p&gt;

&lt;p&gt;那平方呢？平方的话，可以认为速率需要乘以变量自身，几倍方就是几倍速率&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法23.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-20251017195936443.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210412149-539183966.png) --&gt;&lt;/p&gt;

&lt;p&gt;于是就有&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法24.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-20251017195914206.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210423601-970417679.png) --&gt;&lt;/p&gt;

&lt;p&gt;回顾一下，整条式子算起来就是&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法25.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-202510171959261168.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210437902-462484478.png) --&gt;&lt;/p&gt;

&lt;p&gt;是不是看到这里感觉损失函数确实不是随便选的，刚好这个函数能够非常好的适应求导的结果。求导结果里面非常方便进行计算，计算机看了非常开森，因为求导结果是两个已经计算出结果的值，而且只做减法。在第一轮训练结果里面，相信大家能够直接口算结果&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法26.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-202510171959404817.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210449957-612244012.png) --&gt;&lt;/p&gt;

&lt;p&gt;看吧，没骗大家，只有过程推导用到了一点高数的知识，最终计算都是小学到高中的知识，且大部分都是小学知识&lt;/p&gt;

&lt;p&gt;希望看到这里的大家会有很多信心，接下来开始解下一个偏导内容，这是最难的一个部分了&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法27.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-202510171959533245.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210522992-1266133853.png) --&gt;&lt;/p&gt;

&lt;p&gt;激活函数求导过程如下，咱选用的激活函数都是现成的，有很多大佬帮忙求导过了的函数的。如果大家感觉这个过程有点难，那也可以跳过，直接看求导结果就可以了&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法28.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-2025101720062461.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210538250-430685616.png) --&gt;&lt;/p&gt;

&lt;p&gt;可以看到激活函数求导结果也是非常开森的计算，直接就是 fx·(1-fx) 的值，计算非常简单。试试代入公式&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法29.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-20251017200227067.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210550027-797520852.png) --&gt;&lt;/p&gt;

&lt;p&gt;看起来这也是非常简单的小学数学就能完成计算结果，虽然本次计算过程有点难，但结果却是简单的。也依然是计算机非常开森的计算表达式，都是已知的变量的基础加减乘除计算&lt;/p&gt;

&lt;p&gt;最后一步的求导就特别简单啦&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法30.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-20251017200351212.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210608548-2119080656.png) --&gt;&lt;/p&gt;

&lt;p&gt;因为只对w31求偏导，于是 w32 就可以视为常量。本身y0和y1就是被视为常量，直接 w32 乘以 y1就算出0的值。而y0作为w31的系数，求导结果就是y0了。这个过程看起来很解压。好了，各个式子就在这里求完了，将其组合起来&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法31.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-20251017200498079.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210628980-896725353.png) --&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;dc_dw31&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt; &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;y2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y0&lt;/span&gt;&lt;span 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;第一步的求偏导计算咱算了很久，后续的步骤咱就能快非常多了。如对w32求偏导，可以看到只有最后一条式子有所不同&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法32.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-2025101720148758.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210659903-797147596.png) --&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;dc_dw32&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt; &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;y2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;从这里也可以看到对 w31 和 w32 求偏导，也只有最后一个乘数不相同而已。对于程序猿来说，看到有很多相同的代码，本能地自然就会去想优化，这是非常正确的，那就作为课后作业给到大家去优化啦&lt;/p&gt;

&lt;p&gt;下一步尝试求w11的偏导，这一次链式过程就稍微长了一些了，如以下示意图&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法33.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-20251017201172760.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210726484-1026847962.png) --&gt;&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法34.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-20251017201283932.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210734556-50601887.png) --&gt;&lt;/p&gt;

&lt;p&gt;有示意图，相信大家很容易就知道了上面这条表达式是如何写出来的。核心关键点就是倒推，看看w11在哪一级表达式中，然后再继续往上找，直到能到达C损失函数里去。这个过程就类似于已知树（图论的树）进行求到根的路径的过程。求导数的前面两项咱已经在w32求偏导中计算过了，其结果如下&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法35.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-20251017201422682.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210746483-846748999.png) --&gt;&lt;/p&gt;

&lt;p&gt;接下来的各项的求导过程如下&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法36.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-202510172022221.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210802695-770537745.png) --&gt;&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法37.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-2025101720219683.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210809681-515794504.png) --&gt;&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法38.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-20251017202313971.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210817933-1834293148.png) --&gt;&lt;/p&gt;

&lt;p&gt;将几个表达式合起来，即可计算结果&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法39.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-20251017202431886.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210830089-1742550186.png) --&gt;&lt;/p&gt;

&lt;p&gt;表达式虽然长，但拆开看却非常清晰，如下图所示&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法40.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-20251017202544340.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210837299-1386523797.png) --&gt;&lt;/p&gt;

&lt;p&gt;以上的各个变量参数都是已经计算出结果的，其整个表达式最终结果也是非常简单的小学计算题。计算结果是 0.000929 的值&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;dc_dw11&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt; &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;y2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w31&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y0&lt;/span&gt; &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;y0&lt;/span&gt;&lt;span class=&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;同理来求 w12 的偏导，接近和 w11 相同，其差别只有最后的一步计算。如下示意图所示&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法41.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-2025101720376041.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210902949-1519821038.png) --&gt;&lt;/p&gt;

&lt;p&gt;可见计算公式为&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法42.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-20251017203208325.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210915410-1968544291.png) --&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;dc_dw12&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt; &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;y2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w31&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y0&lt;/span&gt; &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;y0&lt;/span&gt;&lt;span class=&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;同理也可以继续求w21的偏导&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法43.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-20251017203338181.jpg&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210937549-521807841.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;通过以上示意图可知&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法44.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-20251017203446404.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831210951498-158847729.png) --&gt;&lt;/p&gt;

&lt;p&gt;各自代入可得&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法45.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-20251017203554082.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831211001018-17218481.png) --&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;dc_dw21&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt; &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;y2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w32&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y1&lt;/span&gt; &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;y1&lt;/span&gt;&lt;span class=&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;按照以上求偏导方式，可得&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法46.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-2025101720484888.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831211024137-322476112.png) --&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;dc_dw22&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt; &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;y2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w32&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y1&lt;/span&gt; &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;y1&lt;/span&gt;&lt;span class=&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;最后将各个权重减去对应的偏导，更新之后的权重我给它上面多标了一瞥&lt;/p&gt;

&lt;!-- ![](image/手算神经网络 BP 传播算法/手算神经网络 BP 传播算法47.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-20251017204212337.jpg&quot; alt=&quot;&quot; /&gt;
&lt;!-- ![](https://img2024.cnblogs.com/blog/1080237/202508/1080237-20250831211051933-1703838752.png) --&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;w31&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w31&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dc_dw31&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;w32&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w32&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dc_dw32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;w11&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w11&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dc_dw11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;w12&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w12&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dc_dw12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;w21&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w21&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dc_dw21&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;w22&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w22&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dc_dw22&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;按照更新的值再跑一遍计算，可得到y2输出为 0.6820 的值，求损失函数为 0.0165 的值，可以看出比原来的第一遍计算得到的 0.0181 更小，证明更加贴近。好在我过程中顺带编写了代码，可以愉快地让计算机帮我跑个100次，跑了100次之后的各个值如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;w11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.099497286575324431&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;w12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.79870730833654868&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;w21&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.35650028026826369&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;w22&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.48814357783267731&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;w31&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.30047225429264057&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;w32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.32533602822418006&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.50076396515258481&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;m&quot;&gt;2.9182137718196697E-07&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可见看到此时输出的 y2 已经非常贴近预期的输出了。于是大家可以开森地认为咱手算训练出一个能够当输入为 x0=0.35 x1=0.9时，获得0.5输出值的简单BP神经网络啦&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;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.35&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;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x1&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;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_out&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;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;n&quot;&gt;x0&lt;/span&gt;&lt;span class=&quot;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;n&quot;&gt;x1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w11&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.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;w12&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.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;w21&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w22&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w31&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.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;w32&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;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;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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;z0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w11&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;w12&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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;z1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w21&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;w22&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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y0&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;n&quot;&gt;z0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y1&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;n&quot;&gt;z1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;z2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w31&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w32&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y2&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;n&quot;&gt;z2&lt;/span&gt;&lt;span class=&quot;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;nf&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&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;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.0000001&lt;/span&gt;&lt;span class=&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;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dc_dw31&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt; &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;y2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y0&lt;/span&gt;&lt;span class=&quot;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_dw32&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt; &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;y2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y1&lt;/span&gt;&lt;span class=&quot;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_dw11&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt; &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;y2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w31&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y0&lt;/span&gt; &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;y0&lt;/span&gt;&lt;span class=&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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dc_dw12&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt; &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;y2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w31&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y0&lt;/span&gt; &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;y0&lt;/span&gt;&lt;span class=&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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dc_dw21&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt; &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;y2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w32&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y1&lt;/span&gt; &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;y1&lt;/span&gt;&lt;span class=&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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dc_dw22&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y2&lt;/span&gt; &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;y2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w32&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y1&lt;/span&gt; &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;y1&lt;/span&gt;&lt;span class=&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;w31&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w31&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dc_dw31&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;w32&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w32&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dc_dw32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;w11&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w11&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dc_dw11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;w12&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w12&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dc_dw12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;w21&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w21&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dc_dw21&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;w22&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w22&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dc_dw22&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;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;kt&quot;&gt;double&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;kt&quot;&gt;double&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;k&quot;&gt;return&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;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;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;Math&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;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;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;C&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;y2&lt;/span&gt;&lt;span class=&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;1.0&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;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;y2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_out&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;也许此时有人会说，明明我看到的py代码量更少，为什么换成C#的代码量就这么多了？别急，大家试试看回忆一下上文布置的课后作业。之所以网上看到的很多py实现的版本的代码量少，是因为使用了本文没有付出场费的矩阵来帮忙计算，许多重复的计算逻辑被封装起来了。试试看给以上的代码进行优化，将重复的计算进行合并，将重复的结构进行抽象，很好，此时相信大家一和py代码进行对比就会发现接近相同啦&lt;/p&gt;

&lt;p&gt;本文代码放在 &lt;a href=&quot;https://github.com/lindexi/lindexi_gd/tree/3621fbea66658c99f11ed4faa28aa60bb5ac4466/Bp/DewhigarjejelDaykogiqem&quot;&gt;github&lt;/a&gt; 和 &lt;a href=&quot;https://gitee.com/lindexi/lindexi_gd/blob/3621fbea66658c99f11ed4faa28aa60bb5ac4466/Bp/DewhigarjejelDaykogiqem&quot;&gt;gitee&lt;/a&gt; 上，可以使用如下命令行拉取代码。我整个代码仓库比较庞大，使用以下命令行可以进行部分拉取，拉取速度比较快&lt;/p&gt;

&lt;p&gt;先创建一个空文件夹，接着使用命令行 cd 命令进入此空文件夹，在命令行里面输入以下代码，即可获取到本文的代码&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;git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 3621fbea66658c99f11ed4faa28aa60bb5ac4466
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上使用的是国内的 gitee 的源，如果 gitee 不能访问，请替换为 github 的源。请在命令行继续输入以下代码，将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码，可以发邮件向我要代码&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;git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 3621fbea66658c99f11ed4faa28aa60bb5ac4466
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;获取代码之后，进入 Bp/DewhigarjejelDaykogiqem 文件夹，即可获取到源代码&lt;/p&gt;

&lt;p&gt;更多技术博客，请参阅 &lt;a href=&quot;/post/%E5%8D%9A%E5%AE%A2%E5%AF%BC%E8%88%AA.html&quot;&gt;博客导航&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 07 Apr 2026 22:02:15 +0000</pubDate>
        <link>/post/%E6%89%8B%E7%AE%97%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C-BP-%E4%BC%A0%E6%92%AD%E7%AE%97%E6%B3%95.html</link>
        <guid isPermaLink="true">/post/%E6%89%8B%E7%AE%97%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C-BP-%E4%BC%A0%E6%92%AD%E7%AE%97%E6%B3%95.html</guid>
        
        
      </item>
    
      <item>
        <title>Windows 调试工具课程</title>
        <description>&lt;p&gt;Windows 调试工具课程——在软件万种死法中调试出原因&lt;/p&gt;

&lt;!--more--&gt;

&lt;!-- CreateTime:2024/09/19 21:08:10 --&gt;
&lt;!-- 发布 --&gt;
&lt;!-- 博客 --&gt;
&lt;!-- 置顶1 --&gt;

&lt;p&gt;本文是我在集团内部上的课程记录而成的博客内容。在本次课程里面将和大家介绍一些在 Windows 上常用的调试工具，以及调查问题的常见套路。适合于伙伴们入门 Windows 调试&lt;/p&gt;

&lt;p&gt;本文内容的组织方式是按照原本课程课件里面的一页页的内容组装而来的方式组织的，在过程中补充一些讲课时的内容&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;注：&lt;/strong&gt; 如果你看不到图片，请确保了允许 http 图片内容（Make sure enable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Insecure origins treated as secure&lt;/code&gt; for my blog）&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程0.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919842595851.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;本次课程里面核心的内容是调试工具，调试工具是我们在调试软件的时候的利器，通过调试工具我们可以找到软件的问题，解决软件的问题&lt;/p&gt;

&lt;!-- 今天来讲一个调试故事，故事是从用户反馈软件用不了的问题开始 --&gt;
&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程1.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919843463623.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;本次的课程的开始我来和大家讲一个调试故事，这个故事是从用户反馈软件用不了的问题开始的&lt;/p&gt;

&lt;!-- 需求分析 --&gt;
&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程2.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919844179826.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;用户说软件用不了，那可能会是什么问题呢？用户不是专业的开发人员，他们不知道如何准确的表述问题&lt;/p&gt;

&lt;!-- 调查思路 --&gt;
&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程4.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919845292516.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;学过软件工程的同学应该有不少，软件工程里面应该会有提到，开发的第一步也是非常关键的一步就是需求分析。当收到用户反馈说软件用不了时，用户在说什么呢？是不是可能是软件崩溃了？还是软件无法启动？还是其他的问题&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程5.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491984546192.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;遇到用户说软件用不了的时候，咱可以有哪些入手点呢？我的调查思路是分为两个大的方向。第一个方向是从当下的情况入手。如果当下已经没有了现场了，则可以考虑第二个方向，复现（重现）问题&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程6.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491984625143.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;从第一个方向入手时，可以先考虑从用户的设备上寻找痕迹。接下来我将和大家聊聊如何开始从用户的设备上寻找痕迹。当然了，如果这个用户是咱的测试人员或者是咱的同事，那寻找痕迹这一步就更有价值了&lt;/p&gt;

&lt;!-- Windows 是咱的好朋友 --&gt;
&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程3.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919844397864.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在用户设备上寻找痕迹时，别忘了 Windows 是咱的好朋友。Windows 提供了很多工具，可以帮助我们找到问题的原因。接下来我将和大家介绍一些 Windows 上自带的常用的调试工具&lt;/p&gt;

&lt;!-- 寻找痕迹-事件查看器 --&gt;
&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程7.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919846288825.jpg&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;/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;错误应用程序名称: lindexi.exe，版本: 5.1.12.63002，时间戳: 0xedd2d687
错误模块名称: MSVCR100.dll，版本: 10.0.40219.325，时间戳: 0x4df2be1e
异常代码: 0x40000015
错误偏移量: 0x0008d6fd
错误进程 ID: 0x994
错误应用程序启动时间: 0x01d50ac3bd970061
错误应用程序路径: C:\Program Files\lindexi\lindexi.exe
错误模块路径: C:\Program Files\PowerShadow\App\MSVCR100.dll
报告 ID: a0c5c0b1-76b7-11e9-9d20-94c69123de40
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;!-- 举个栗子 --&gt;
&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程8.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919846521393.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;细心的伙伴也许一眼就看出来问题了，出现问题的是 MSVCR100.dll 模块，然而这个模块路径居然是在一个不认识的，名为 PowerShadow 的软件的目录下。这时候就可以大概确定问题了，这是被投毒了&lt;/p&gt;

&lt;p&gt;试试用谷歌好帮手，搜搜这个软件是什么软件。刚好搜到了这篇博客： &lt;a href=&quot;/post/%E5%BD%B1%E5%AD%90%E7%B3%BB%E7%BB%9F%E8%AE%A9-C++-%E7%A8%8B%E5%BA%8F%E6%97%A0%E6%B3%95%E8%BF%90%E8%A1%8C.html&quot;&gt;影子系统让 C++ 程序无法运行&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;于是这就结束战斗了，调查到了问题的原因，软件无法启动是因为被投毒了，被影子系统投毒了。解决方法就是请用户卸载影子系统，因为影子系统也不维护了，咱软件层没啥好挣扎的&lt;/p&gt;

&lt;!-- 事件查看器 - 日常不工作 --&gt;
&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程9.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919847156085.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可惜的是在很多用户的设备上，事件查看器日常不工作。没关系，能从事件查看器找到额外信息，就是赚到了&lt;/p&gt;

&lt;p&gt;如果事件查看器找不到或不能用？咱还有其他很多工具可以用&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程10.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919847302677.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;寻找痕迹的时候，另一个常用的好工具就是任务管理器。任务管理器是 Windows 自带的一个工具，可以帮助我们了解到非常多的信息&lt;/p&gt;

&lt;p&gt;通过任务管理器寻找痕迹时，可以按照如上图所示的决策树了解一下情况。如果不能在任务管理器里面看到进程，那很可能就是进程已经崩掉了。如果能够看到进程，那可能就是进程卡了。此时关注点可以是 CPU 使用率。如果 CPU 使用率不动，那可以猜猜可能是死锁问题，如果 CPU 使用率爆高，那可能是死循环等问题。同步也看一下内存使用率，虽然在任务管理器里面看内存使用率不能真实反映内存使用情况，但是可以作为一个参考。详细关于如何正确查看程序的内存使用情况，后面会有专门的内容介绍&lt;/p&gt;

&lt;p&gt;无论是何种情况，都可以试试捞一个 DUMP 回来调试看看。当然了，对于软件崩掉的情况，先尝试一下是不是能启动起来，拼手的速度快速捞一个 DUMP 回来，如果不能，那后文还会和大家介绍其他工具来辅助捞 DUMP 文件&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程11.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919847441750.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;先回顾一下，咱的调查思路一开始就是尝试寻找痕迹。寻找痕迹的时候借助 Windows 里面提供的好用的工具，这里重点介绍的是事件查看器和任务管理器。通过事件查看器可以快速的了解到软件崩溃的原因，通过任务管理器可以了解到软件的运行情况&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程12.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20249198480955.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在通过自带的工具没有明确收获的情况下，则尝试捞一个 DUMP 回来开发机器上进行进一步分析&lt;/p&gt;

&lt;p&gt;本课程这里提到的 DUMP 文件是指 Windows 下的内存转储文件，是一个二进制文件，简单用人话说就是将进程的内存内容保存到文件里面。通过 DUMP 文件可以有效还原出此时的进程的内存状态和内存里面的内容，可以用于进一步的分析。当用户环境里面没有带开发工具时，捞一个 DUMP 文件回来，可以帮助我们在开发机器上进行进一步的分析。捞 DUMP 分析的过程，相当于给进程做了一个快照，然后将其放在开发机器上进行进一步的分析&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程13.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919193513102.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;假设进程还在的话，那最简单的捞 DUMP 方式就是通过任务管理器右键选择创建内存转储文件了。对应的英文系统是 Create memory dump file 菜单项&lt;/p&gt;

&lt;p&gt;这里需要额外说明的是，如果当前系统是 x64 系统，但是自己的进程是 32 位进程，那此时不建议使用默认打开的任务管理器捞 DUMP 文件。因为默认打开的任务管理器是 x64 的，打出来的是 x64 转储文件，包含 WoW64 子系统的信息。详细请看 &lt;a href=&quot;https://zhuanlan.zhihu.com/p/103381060&quot;&gt;你生成的转储文件有问题吗？ - 知乎&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;正确的做法应该是使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C:\Windows\SysWOW64\Taskmgr.exe&lt;/code&gt; 的任务管理器去捞 DUMP 文件&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程14.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919848326712.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;现在假定捞到了 DUMP 文件了，那接下来的步骤就是如何分析 DUMP 文件了。当然了，前置步骤就是如何将 DUMP 文件传回到自己的开发机器上，这里有一个小妙招就是将这个 DUMP 压缩一下。由于 DUMP 文件是内存转储文件，大部分都是全零的内容，压缩率非常高。如果需要通过网络等方式传输，那压缩一下再传输会快很多&lt;/p&gt;

&lt;!-- 如何分析 DUMP 文件 --&gt;
&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程15.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919848549055.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;分析 DUMP 的工具有很多，我着重要和大家介绍的是太阳系最强 IDE —— VisualStudio。VisualStudio 已经是一个成熟的 IDE 了，只需将 DUMP 拖进去就可以了，聪明的 VisualStudio 可以自动帮咱进行分析&lt;/p&gt;

&lt;!-- 将 DUMP 拖入到 VisualStudio 的界面 --&gt;
&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程16.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919849116862.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;一般而言，将 DUMP 拖入到 Visual Studio 里面，接着点击混合调试按钮即可。混合调试是使用 托管 调试和 本机 调试的组合。托管调试是指调试 .NET 程序，本机调试是指调试其他非 .NET 系的程序。混合调试是指同时调试托管和本机代码，因为一般而言 .NET 系的应用要在托管层崩溃是有点难度的，除非开发者自己比较缺乏处理。然而本机代码，如某些使用 C 、汇编、C++ 编写的程序，那就容易崩溃了。混合调试可以同时调试这两种代码。即使进程完全不是 .NET 程序，也可以使用混合调试来调试&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程97.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20249191945597805.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;进入混合调试之后，需要等待 Visual Studio 自动分析。如果是第一次调试 DUMP 文件的，可能会在下载符号这一步卡住一会。大家可以出去喝个茶，等待一下，再回来看看。实在等不急了，那就点击取消符号加载再继续吧&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程18.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919849549120.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;好的，现在咱的进度就是在用户侧发现了问题，且不能通过事件查看器等结束战斗。将用户的 DUMP 文件捞回来，通过 Visual Studio 进行分析。分析的方法就是将 DUMP 文件拖入 Visual Studio 里面，然后点击混合调试按钮。等待 Visual Studio 自动分析，即可看到分析结果&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程19.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491985074456.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;那聪明的 Visual Studio 会帮咱分析出什么内容呢？如何看 Visual Studio 的分析结果呢？常见的套路就是关注 Visual Studio 以下三个方面内容&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;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程20.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919850225309.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;先来和大家介绍一下调用堆栈。调用堆栈是个好东西，调用堆栈是一个非常重要的内容，可以帮助我们了解到程序是如何运行的。通过调用堆栈可以看到程序是如何运行的，是从哪个函数开始的，是如何调用的，是如何返回的。默认的 Visual Studio 调试布局里面，可以快速看到调用堆栈窗格&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程21.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919850346537.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;调用堆栈可以如何看？调用堆栈可以和着之前在用户端任务管理器所见内容进行一起分析。如在任务管理器看不见进程，即对应进程崩了的问题，可以通过调用堆栈尝试看到是谁带崩的，崩之前调用的是哪个函数。如果是在任务管理器能看到进程，但是 CPU 使用率不动，那可能是死锁问题，可以通过调用堆栈看到是哪个函数卡住了主线程或进入锁。如果是 CPU 使用率爆高，那可能是死循环问题，可以通过调用堆栈看到是哪个函数跑满了线程&lt;/p&gt;

&lt;!-- 堆栈是个好东西 谁带崩的 - 任务管理器已看不到进程 --&gt;
&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程22.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919850513991.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;举个真实的例子，以下就是我从用户端捞回来的一个 DUMP 文件。通过 Visual Studio 分析，崩溃之前的调用堆栈如下&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;&amp;gt;	00000000()	Unknown
 	[Frames below may be incorrect and/or missing]	Unknown
 	nvumdshim.dll!710d0745()	Unknown
 	nvd3dum.dll!5989f2e1()	Unknown
 	nvd3dum.dll!595f1716()	Unknown
 	nvd3dum.dll!596b7827()	Unknown
 	nvd3dum.dll!598a6233()	Unknown
 	nvd3dum.dll!5989b95c()	Unknown
 	nvd3dum.dll!5989c33b()	Unknown
 	nvd3dum.dll!598816bc()	Unknown
 	nvumdshim.dll!710ca40e()	Unknown
 	nvumdshim.dll!710cbb78()	Unknown
 	nvumdshim.dll!710ca17f()	Unknown
 	nvumdshim.dll!710ca0d3()	Unknown
 	d3d9.dll!5ab86f81()	Unknown
 	ntdll.dll!_NtWaitForMultipleObjects@20 ()	Unknown
 	KERNELBASE.dll!76f69723()	Unknown
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;通过调用堆栈可以看到是 nvumdshim.dll 模块带崩的。这个模块是 NVIDIA 显卡驱动的模块。通过这个调用堆栈可以看到是 NVIDIA 显卡驱动带崩的。这个问题的解决方法就是更新 NVIDIA 显卡驱动。此问题详细请看 &lt;a href=&quot;/post/%E8%AE%B0%E5%9B%A0%E4%B8%BA-NVIDIA-%E6%98%BE%E9%A9%B1%E9%94%99%E8%AF%AF%E8%80%8C%E8%AE%A9-WPF-%E5%BA%94%E7%94%A8%E5%90%AF%E5%8A%A8%E9%97%AA%E9%80%80%E9%97%AE%E9%A2%98.html&quot;&gt;记因为 NVIDIA 显驱错误而让 WPF 应用启动闪退问题&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;驱动问题是客户端崩的常见问题，表现就是在很多用户电脑工作好好的，在某些用户就起不来&lt;/p&gt;

&lt;p&gt;修复 DirectX 时，我常用的就是 DirectX 修复工具，此工具下载地址是： &lt;a href=&quot;https://blog.csdn.net/VBcom/article/details/6962388&quot;&gt;https://blog.csdn.net/VBcom/article/details/6962388&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;讲完了谁带崩的问题，接下来再看另一个案例。对应 CPU 不动的问题，如下图所示的调用堆栈&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程17.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20249191958274476.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;大家猜猜上面堆栈告诉咱什么问题&lt;/p&gt;

&lt;!-- 堆栈是个好东西 谁卡住了我的主线程 - CPU 不动 --&gt;
&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程23.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491985113140.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;通过以上的堆栈可以知道进入了锁。此时的常见套路就是从上到下找找，找第一个咱自己程序集的调用函数，如这里就找到了是在 lindexi.dll 里面的方法。可以知道的是这个方法有逻辑在等待锁，且这个锁就不返回。此时配合代码食用更佳。咱这里能够知道进程卡住的原因是因为等待锁，且这个锁不返回，而至于这个锁在业务上是什么作用就需要咱进一步配合代码进行分析了&lt;/p&gt;

&lt;!-- 堆栈是个好东西 谁跑满了线程 - CPU 爆高 --&gt;
&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程24.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919851324111.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;再来看看对应 CPU 爆高的一个案例，此时堆栈里面的信息可以告诉咱，现在正在跑的方法是哪些。有可能就是当前的调用堆栈的顶部的几个方法有逻辑跑满了线程了。同样，此时配合代码食用更佳&lt;/p&gt;

&lt;p&gt;但有可能此时面对的情况是没有代码。如使用的是第三方库等，此时靠堆栈信息是不够的。先让大家思考这个问题，如果此时没有代码还可以如何进一步分析？我将在后文和大家介绍如何通过三板斧来进一步分析&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程25.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919851455017.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;回顾一下，这就是咱拖入 DUMP 文件之后，依靠 Visual Studio 里面的调用堆栈进行问题分析的常见三个案例。对应软件崩溃的问题，可以通过调用堆栈看到是谁带崩的。对应 CPU 不动的问题，可以通过调用堆栈看到是谁卡住了主线程。对应 CPU 爆高的问题，可以通过调用堆栈看到是谁跑满了线程&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程26.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919851592025.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;但是仅靠调用堆栈可能还是不够的，有时候需要更多的信息。接下来我将和大家介绍如何通过“三板斧”来进一步分析&lt;/p&gt;

&lt;p&gt;这里介绍的“三板斧”分别是寄存器、反汇编、内存这三个方面的工具。通过这三个方面的工具可以帮助我们进一步的分析问题&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程27.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919852129168.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;需要说明的是用到这三个工具时仅仅只是在咱有需要了解更多状态信息的时候。而且通过这三个工具也不一定能够准确了解到问题的原因。这三个工具的使用本身不难，但是其难点确是这几个工具所见内容的背后大家关于程序本身的理解以及软件运行机制的了解。如果对于软件运行机制不了解，那这三个工具所见内容可能会让人难以理解，或者是调查方向跑偏&lt;/p&gt;

&lt;!-- 这个方法有逻辑跑满了，跑了什么？ --&gt;
&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程28.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919852309943.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;依然使用刚才的例子，当看到 CPU 爆高的时候，通过调用堆栈可以看到是哪个方法跑满了线程。但是这个方法逻辑跑满了，其原因是什么呢？调用堆栈可无法回答此问题&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程29.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919201056644.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;试试先在 Visual Studio 里面打开内存、寄存器、反汇编窗格。这三个工具可以帮助我们进一步分析问题&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程30.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20249198538404.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;打开之后的 Visual Studio 的界面布局大概如上图所示&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程31.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20249192012578371.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;拿本课程的 CPU 爆高的例子，先通过反汇编发现了可能存在的问题，如想看看 rcx 寄存器里面存放了什么。通过寄存器窗格可以看到 rcx 寄存器里面存放了什么内容。通过内存窗格可以看到这个地址里面存放了什么内容。刚好就看到了对应的内存里面存放了一段逗比代码&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程32.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919853377084.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;使用 “三板斧” 本身的难度不大，但是其难点在于其背后的知识。如汇编知识，寄存器的机制，以及软件本身的运行机制。这部分知识远远超过了本课程能介绍的范围，需要大家自行学习，但由于这部分知识的学习成本较高，所以在实际工作中，这部分知识可能并不是必须的。我只敢推荐大家在有余力的情况下进行学习，如果平时工作已经很忙了学不过来了，那这部分知识还可以先放着。但是如果能够掌握这部分知识，那在调试问题时会有所帮助&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程33.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919853515312.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;继续和大家介绍 Visual Studio 的另一个调试工具——局部变量。局部变量也是个好东西，可以帮助我们了解到程序运行时的状态。通过局部变量可以看到程序运行时的变量的值，可以帮助我们了解到程序运行时的状态&lt;/p&gt;

&lt;!-- 局部变量也是好东西 - 错误码含义哪里找 --&gt;
&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程34.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919854177713.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如看到了错误之前的局部变量有一个名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lastErrorCode&lt;/code&gt; 的变量，也许可以通过这个变量的值来了解到错误的原因。但是这个错误码是什么意思呢？这个错误码的含义在哪里找呢？咱可以试试 error 这个工具，这个工具可以自动帮助咱找到可能的错误码的含义。这是工具是微软整理的，绝大部分调用系统层的组件所见的错误码都可以在这里找到&lt;/p&gt;

&lt;p&gt;工具下载地址： &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/debug/system-error-code-lookup-tool&quot;&gt;https://learn.microsoft.com/en-us/windows/win32/debug/system-error-code-lookup-tool&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;如在这里咱可以看到的错误信息是文件或文件夹名错误，根据咱的业务逻辑，可能是文件名错误导致的问题。那接下来的调查方向就是看看为什么出现错误的文件名了，这时候也许一看代码就理解了&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程36.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491985518809.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;再举另一个真实的例子，如看到的是如上图的异常导致的崩溃。根据咱通过搜索引擎了解到的知识，这个 WindowsCodecs.dll 是 Windows 系统的 WIC 多媒体解码层。可能此时遇到的问题和图片等多媒体的编解码有关&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程35.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20249192029147352.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;刚好在本例子里面，通过局部变量看到了出问题的图片的文件地址，此时的调查就更加有方向了。除了可能存在的 WIC 层的问题外，还可以是图片文件本身的问题。如图片文件投毒等问题&lt;/p&gt;

&lt;!-- 图片/音视频投了什么毒？延伸一下 --&gt;
&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程37.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20249198552013.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;延伸一下，如何了解图片、音视频等文件是否被投毒了？这里推荐一个工具，通过 MediaInfo 工具可以帮助咱看到文件的许多信息&lt;/p&gt;

&lt;p&gt;如这个文件就是一个假装是 png 的 WebP 文件，然后投毒将 WIC 层搞崩了&lt;/p&gt;

&lt;p&gt;MediaInfo 工具下载地址： &lt;a href=&quot;https://mediaarea.net/en/MediaInfo/Download&quot;&gt;https://mediaarea.net/en/MediaInfo/Download&lt;/a&gt;&lt;/p&gt;

&lt;!-- 好像… 还是有些问题调试不出来 --&gt;
&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程38.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491985534673.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;好像… 还是有些问题调试不出来&lt;/p&gt;

&lt;p&gt;太阳系最强 IDE 也顶不住呀&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程39.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919855526129.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;那就试试上接近能调试一切的 WinDbg 吧&lt;/p&gt;

&lt;p&gt;这个工具非常强大，只是有一个问题。那就是有亿点点上手门槛&lt;/p&gt;

&lt;p&gt;在这里我告诉大家一个非常简单的方法，让大家瞬间就能学会上手使用 WinDbg 工具调试问题。方法就是请一个熟悉 WinDbg 的伙伴，让他帮你调试，找到一个工具人帮你使用 WinDbg 调试问题是最快能学会使用 WinDbg 的方法&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程40.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491985665115.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;回顾一下，以上咱就聊了在用户端发现问题，先尝试使用 Windows 自带工具快速进行定位问题。以及捞到 DUMP 文件之后，如何在开发机器上通过 Visual Studio 进行进一步分析。分析的方法就是将 DUMP 文件拖入 Visual Studio 里面，然后点击混合调试按钮。等待 Visual Studio 自动分析，即可看到分析结果。分析的重点是调用堆栈、三板斧、局部变量。通过这三个方面的工具可以帮助我们进一步的分析问题&lt;/p&gt;

&lt;p&gt;如果 Visual Studio 还不能解决问题，那就找个工具人来帮忙使用 WinDbg 继续调查问题&lt;/p&gt;

&lt;p&gt;这就是第一个大方向的内容&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程41.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919856197861.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;第二个大方向就是事后现场的复现问题。什么时候需要复现问题？比如最简单来说就是软件启动即崩溃，完全来不及打开任务管理器捞 DUMP 文件。这时候就需要复现问题了，通过复现问题可以帮助我们更好的定位问题&lt;/p&gt;

&lt;p&gt;复现问题时也不是只是简单重复跑程序，而是可以通过更多的工具辅助来在复现问题时更好的定位问题&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程42.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919856333791.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;首要介绍的就是 ProcDump 工具&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程43.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919856493397.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当使用任务管理器捞不到 DUMP 或不好捞 DUMP 时，使用 ProcDump 工具能够更好的帮助我们捞 DUMP 文件。ProcDump 工具是 Sysinternals 的工具，下载地址是： &lt;a href=&quot;https://learn.microsoft.com/zh-cn/sysinternals/downloads/procdump&quot;&gt;https://learn.microsoft.com/zh-cn/sysinternals/downloads/procdump&lt;/a&gt;&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程44.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491985745255.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;为什么说有时候不好使用任务管理器捞 DUMP 呢？因为现实往往很复杂。除了闪崩，软件启动即崩溃导致的手速不够快，捞不到 DUMP 文件之外，还有其他很多问题。比如软件就是处于似崩未崩的状态，期望抓到某个时机的状态，如软件一定会在某次 CPU 爆高之后不能符合预期工作，然而 CPU 爆高的时间非常短，靠人类去看去抓是有些废程序猿的。比如软件半夜崩溃，只有在午夜12点才会崩溃，这时候人类可能已经睡着了，即使没睡着，可能错过了这个时间点就要等明天的午夜12点了。再比如是非必现的问题，需要压测才能复现，期望自动化收集，否则可能要跑几千次才能复现一次，靠人类手工一次次一个个去捞的工作量有些大&lt;/p&gt;

&lt;!-- 如何在程序万种死法中有效的生成 Dump 文件 --&gt;
&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程45.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919857308659.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;通过 ProcDump 可以在程序万种死法中有效的生成 Dump 文件，只需使用好 ProcDump 的参数。具体参数作用可以参考 &lt;a href=&quot;https://learn.microsoft.com/zh-cn/sysinternals/downloads/procdump&quot;&gt;微软官方文档&lt;/a&gt; 和 &lt;a href=&quot;https://www.cnblogs.com/huangxincheng/p/14661031.html&quot;&gt;如何在 NET 程序万种死法中有效的生成 Dump (上) - 一线码农 - 博客园&lt;/a&gt;&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程46.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919857465731.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这是一个小游戏，让大家连连线，看看在什么情况下应该使用什么方法&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程47.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491985801006.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在调查思路这里，复现问题时经常伴随使用 ProcDump 工具，因为 ProcDump 工具可以在非常多的情况下帮助我们捞 DUMP 文件&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程48.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491985814719.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;复现问题时，不仅只有 ProcDump 工具。还有可能面对的是事后现场的情况，此时需要使用更多的工具来辅助定位问题。以及当没有调查思路时，可以试试常见的问题的探索帮助寻找思路&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程49.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919858276197.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;来和大家讲讲事后现场的调查&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程50.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919858402700.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;什么是事后现场？事后现场问题在这里一般说的是当前的现场或能复现所抓取到的现场已经不是问题发生的现场，而是发生问题之后的现场了&lt;/p&gt;

&lt;p&gt;比如找到问题了，但问题非本质问题。常见的就是通过 DUMP 分析是如 空 异常的情况，导致崩溃的原因是因为空指针异常。但是空指针异常是如何产生的呢？这时候就需要通过事后现场分析思路调查来进一步分析问题&lt;/p&gt;

&lt;p&gt;比如发生问题的地方不是产生问题的地方。如本课程的例子里面，崩溃原因是一张假装 png 的 WebP 图片，那这张图片是哪里来的，为什么会使用这张图片。如果此时代码逻辑没有帮助的话，那就需要进一步通过复现问题调查事前现场来进一步分析问题&lt;/p&gt;

&lt;p&gt;比如系统性的问题。常见的就是团伙作案，不是单个应用导致的问题。这类问题的难度在于其复杂度，可能难以抓到正确的现场。此时也需要通过多次复现问题，抓取更多的信息，通过事前和事后现场的分析来进一步分析问题&lt;/p&gt;

&lt;!-- 事后现场？团伙作案？ --&gt;
&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程51.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491920533676.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;面对事后现场和团伙作案等问题，采用微软极品工具箱的 Process Monitor 工具，配合 DebugView 工具通常都能有不错的收获&lt;/p&gt;

&lt;p&gt;Process Monitor 工具下载地址： &lt;a href=&quot;https://learn.microsoft.com/zh-cn/sysinternals/downloads/portmon&quot;&gt;https://learn.microsoft.com/zh-cn/sysinternals/downloads/portmon&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Debugview++ 工具开源地址： &lt;a href=&quot;https://github.com/CobaltFusion/DebugViewPP&quot;&gt;https://github.com/CobaltFusion/DebugViewPP&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;DebugView 工具下载地址： &lt;a href=&quot;https://learn.microsoft.com/en-us/sysinternals/downloads/debugview&quot;&gt;https://learn.microsoft.com/en-us/sysinternals/downloads/debugview&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;从界面和交互上，DebugView++ 比 DebugView 更好用一些&lt;/p&gt;

&lt;!-- 举个真实栗子 --&gt;
&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程52.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919859209122.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;举个真实栗子来和大家演示多个工具之间的配合使用来调用一个有趣且复杂的问题&lt;/p&gt;

&lt;p&gt;这个问题的开始是测试同学和我报告了触摸失效问题，后来经过进一步调查发现其实是 explorer 未响应问题，表现就是 explorer 迷之闪黑&lt;/p&gt;

&lt;p&gt;这个问题复杂之处在于 explorer 不是咱的，咱也不熟悉，也不知道是什么导致的。而且 explorer 太庞大了，捞到 DUMP 分析压力过大，耗时耗力。需要使用更多的工具辅助进一步分析问题&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程53.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919859335765.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;此时通过 Process Monitor 工具抓取 explorer 进程信息，发现了如上图的有趣的内容。里面很受我关注的就是存在了进程退出&lt;/p&gt;

&lt;p&gt;通过网上四处搜发现 explorer 是一个多进程软件，进程的退出和迷之闪黑可能有所影响。既然进程退出了，那就试试上 ProcDump 工具在进程之前之后抓一个 DUMP 文件回来分析&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程54.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919859437965.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;由于 explorer 十分庞大，且咱也不熟悉 explorer 的代码，来回抓了几次 DUMP 分析都没有什么收获。直到某次抓取到了一个有趣的 DUMP 文件，通过这个 DUMP 文件发现了在进程退出之前的调用堆栈里面包含了 Shell32 的一些调用&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程55.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919859557372.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;再根据前面的 Process Monitor 工具抓到的在进程退出之前碰的是 Realtek Bluetooth 蓝牙模块，于是重心就在 Shell32 和蓝牙一起组合上面&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程56.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20249199099454.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;既然大概定位到这里，那就继续上 ShellView 工具。通过 ShellView 工具进行大量的 Shell32 组件的禁用，我的做法大概就是看哪个不开森就禁用哪个，进行二分法的禁用，最终发现了是 Realtek Bluetooth 蓝牙模块导致的问题&lt;/p&gt;

&lt;p&gt;二分法的禁用就是先一口气禁用一半的组件，看看问题是否解决。如果解决了，那就说明问题在这一半里面。如果没有解决，那就说明问题在另一半里面。然后再在这一半里面继续二分禁用，直到找到问题所在&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程57.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491990244692.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;经过以上的调查工具可以了解到是蓝牙相关模块的问题，集中火力找到明确的调试方向，很快就找到是蓝牙驱动的问题&lt;/p&gt;

&lt;p&gt;详细的调试内容可比这里介绍的有趣的很，请看 &lt;a href=&quot;/post/%E8%AE%B0%E4%B8%80%E6%AC%A1%E8%B0%83%E8%AF%95%E8%B5%84%E6%BA%90%E7%AE%A1%E7%90%86%E5%99%A8%E6%9C%AA%E5%93%8D%E5%BA%94%E7%BB%8F%E9%AA%8C.html&quot;&gt;记一次调试资源管理器未响应经验&lt;/a&gt;&lt;/p&gt;

&lt;!-- 事后现场 --&gt;
&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程58.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491994381765.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;以上的案例里面 Process Monitor 立了首功，通过 Process Monitor 工具抓取了 explorer 进程的信息，发现了进程退出的信息，以及退出之前访问了蓝牙相关模块，最终通过 ShellView 工具禁用了蓝牙模块定位到了具体模块导致的问题。在许多事后现场问题中，都会使用 Process Monitor 工具配合复现，尝试找到故障之前的事前发生了什么，了解故障之前的行为。进而可以缩小定位问题的方向&lt;/p&gt;

&lt;p&gt;Process Monitor 工具适用于辅助调试不熟悉的应用、复杂的情况，通过了解其行为来辅助缩小定位范围，提高调试效率。同时 Process Monitor 工具可以一口气抓多个进程的信息，也常用于辅助定位进程们团伙作案问题&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程59.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20249199458574.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;以上就是调查思路里面的第二大方向的事后现场的内容。其思想核心就是通过复现配合工具抓取现场信息，通过现场信息进一步分析问题&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程60.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919213128726.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;有时候调查问题过程中没有头绪，没有思路。此时可以还可以试试一些常见问题的探索，尝试通过这些常用问题套路和经验找到入手点&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程61.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20249199688371.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这些常见的工具可以帮助我们进行一些常见问题的排查。有时候开发任务紧张，没有充足的时间进行全面的调查，通过常用的套路也可以提高定位效率，不一定我们需要来一次全面的调查，有可能只通过现象和猜测，配合常用套路就能找到问题的原因&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程62.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491996191828.jpg&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;内存问题，包括 OOM 系问题&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;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程63.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491996414208.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;先来聊聊依赖缺失问题&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程64.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491996542473.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;依赖缺失的表现行为很明显，一般就是程序猿常说的，“在我电脑上明明就是好的，为什么到你电脑上就不行了”这类的话，那就可以第一时间怀疑是依赖缺失问题。或者是软件无法启动，启动即崩溃，这类问题也可以怀疑是依赖缺失问题。或者是软件开始能跑，但是跑到某个模块就崩溃了，且很固定的到某个功能模块就崩溃，那此时也能怀疑是依赖缺失问题&lt;/p&gt;

&lt;p&gt;有些不熟悉 Windows 的伙伴也许有这样的问题，明明自己写的应用似乎就没有什么依赖，为什么还可能遇到依赖缺失的问题呢？这是因为 Windows 系统本身就是一个依赖系统，很多功能都是通过依赖来实现的。比如说一个最简单的例子，你写了一个 C# 程序，但是你的 C# 程序里面调用了一个 MessageBox 函数，这个 MessageBox 函数是 Windows 系统的一个函数，你的 C# 程序调用了这个函数，那你的 C# 程序就依赖了 Windows 系统的 MessageBox 函数。如果你的 C# 程序在别的电脑上运行，别的电脑上没有这个 MessageBox 函数，那你的 C# 程序就会崩溃。软件运行过程中，需要依赖许多组件，包括直接依赖和间接依赖。有可能在代码编写阶段没有引入依赖，但实际上软件已经带上了许多隐式依赖内容，如常见的 C++ 程序需要的 VC++ 运行时依赖等等。这些依赖可能会在复杂的用户环境里遭遇投毒问题，导致软件无法正常运行&lt;/p&gt;

&lt;!-- 找不到，找错 DLL --&gt;
&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程65.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20249199783236.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;依赖缺失问题首推是采用 Dependencies 工具，使用这个工具可以帮助我们定位两种类型的问题：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;是否在目标机器上依赖缺失问题&lt;/li&gt;
  &lt;li&gt;是否在目标机器上的依赖被投毒&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dependencies 工具开源地址： &lt;a href=&quot;https://github.com/lucasg/Dependencies&quot;&gt;https://github.com/lucasg/Dependencies&lt;/a&gt;&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程66.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491997303257.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;识别依赖缺失问题的方法就是将自己的应用拖入到 Dependencies 工具里面，然后看看是否有缺失依赖的提示。如上图所示，可以看到这个应用缺乏了一个名为 Lindexi.dll 的依赖。此时可以看看输出文件里面是否真的包含了这个依赖文件，如果没有，那就说明这个依赖确实是缺失的。如果有，那再看看这个依赖文件的二进制是否符合预期，如可能被修改或者下载不全等问题&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程67.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491997422909.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;常用的依赖缺失调查方向套路是看看是否有 C++ 运行时系列的丢失。其中 C++ 运行时系列常见的就是 VC++ 的多个版本的分发库，或者是 VC++ 运行时，即 vcruntime140.dll 文件。以及 msvcp140.dll 文件缺失。其中重点需要说明的是，如果能看到有 vcruntime140d.dll 文件的依赖，那就证明开发侧存在问题，这里的 vcruntime140d.dll 的意思是 VC++ runtime 14.0 debug 版本的 dll 的意思。如果看到有这个 dll 的依赖，证明你的相关方提供给你的 C++ 库是使用 Debug 版本构建的，这是不能给到用户端的。最佳方法是重新让其构建一个 Release 版本的 DLL 给你。只有在实在没有办法的时候，才考虑在用户端配上开发环境&lt;/p&gt;

&lt;p&gt;另一个就是看看是否有 ucrt 系列的丢失，这个常见就是软件能在 Win10 上跑得好好的，在 Win7 上就启动不了。常见就是缺失了 ucrt 系列的依赖。如果是遇到这种问题，简单的做法就是将这些文件拷贝到软件运行目录下，或者是将这些文件打包到软件的安装包里面即可&lt;/p&gt;

&lt;p&gt;最后一个就是看看是否有某些仅在开发机才有的负载，如我就喜欢引用一个名为 Lindexi.dll 的文件，这个依赖文件只在我的电脑上存在，用户设备上是绝对没有的，那自然在用户设备上跑不起来也就符合预期了&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程68.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491997541283.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;使用 Dependencies 工具除了找依赖缺失之外，还可以用来找到是否被投毒的问题。如上图所示，看大家是否能够快速看出来问题&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程69.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20249199851991.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;是的，上图里面包含了一个名为 vcruntime140.dll 文件，包含这个文件是正常的，但是不正常的地方在于其路径。为什么 vcruntime140.dll 加载的居然是在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C:\Program Files (x86)\Foo&lt;/code&gt; 文件夹里，这就证明被投毒了。大家可以在工具里面看看有没有存在不熟悉或奇怪的路径，如果有，那就可能是投毒的问题？这个问题就需要进一步的调查了&lt;/p&gt;

&lt;p&gt;如我记录的 &lt;a href=&quot;/post/%E5%BD%B1%E5%AD%90%E7%B3%BB%E7%BB%9F%E8%AE%A9-C++-%E7%A8%8B%E5%BA%8F%E6%97%A0%E6%B3%95%E8%BF%90%E8%A1%8C.html&quot;&gt;影子系统让 C++ 程序无法运行&lt;/a&gt; 这篇博客提到的就是非常标准的被投毒的问题，大家可以看到 MSVCR100.dll 加载的路径是在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C:\Program Files\PowerShadow\App&lt;/code&gt; 文件夹里面&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程70.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491998185327.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;以上就是常用套路里面的调查依赖缺失问题，不仅仅可能是崩溃等问题，有时候软件运行功能不正常也可以看看是否有依赖缺失或被投毒的问题&lt;/p&gt;

&lt;p&gt;额外地，也可以使用 &lt;a href=&quot;https://www.cnblogs.com/suv789/p/18173393&quot;&gt;sxstrace 工具&lt;/a&gt; 辅助定位启动不了的问题。只是从直观程度上讲，采用 SxsTrace 工具需要去爬日志，不如 Dependencies 工具来得简单。但 &lt;a href=&quot;https://learn.microsoft.com/zh-cn/windows-server/administration/windows-commands/sxstrace&quot;&gt;SxsTrace 工具&lt;/a&gt; 能分析更多其他维度上的问题，如果 Dependencies 不能定位到问题，也好来试试系统自带的 &lt;a href=&quot;https://learn.microsoft.com/zh-cn/windows-server/administration/windows-commands/sxstrace&quot;&gt;SxsTrace 工具&lt;/a&gt; 进行分析&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程71.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491998309059.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;继续看一下内存问题&lt;/p&gt;

&lt;!-- 任务管理器 --&gt;
&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程72.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491998508245.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;对 Windows 内存机制不了解的伙伴也许常常会拿任务管理器里面看到的内存数值当成衡量应用软件运行占用内存的标准。比如我说软件已经 OOM 了，但是对Windows 内存机制不了解的伙伴拿出任务管理器一看，发现应用软件才占用 300MB 内存而已，于是就完全不相信可能存在的 OOM 问题&lt;/p&gt;

&lt;p&gt;这里说的 OOM 问题指的是 OutofMemory 经典问题，直接表现就是内存不足了，代码里面想要申请内存但是申请内存失败&lt;/p&gt;

&lt;p&gt;在开始聊这个话题之前，需要先做一点点科普。如果大家想要看一个符合客观事实的应用软件运行过程的内存使用情况，应该使用 VMMap 工具查看，而不是任务管理器。任务管理器只能当成一个快速查看的工具，而不是一个准确的工具。VMMap 工具可以帮助我们查看应用软件的内存使用情况，包括了虚拟内存、物理内存、私有内存、共享内存等等&lt;/p&gt;

&lt;p&gt;VMMap 工具下载地址： &lt;a href=&quot;https://learn.microsoft.com/zh-cn/sysinternals/downloads/vmmap&quot;&gt;https://learn.microsoft.com/zh-cn/sysinternals/downloads/vmmap&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;应用软件的内存使用是一个相对复杂的话题，不仅仅是看看占用了多少内存就能判断的。因为事实上一个应用软件的运行占用内存是需要分至少三个维度去看的。最直接的维度就是私有内存和共享内存的占用。一般而言，对于共享内存的占用咱是不做多关注的。另一个维度是内存使用种类。其中开发人员在进行应用软件的内存优化时，常常直接触碰的应该就是 Private Data 的 Private 部分。即前面两个维度里面的私有内存和 Private Data 种类。以及 Stack 的 Private 部分。而至于如 Image 等部分，就常常不是开发人员所关注的。这里需要对 Image 特别说明的是，这个单词在这里的意思不是图片的意思，至少不是类似于 png 或 jpg 图片的意思，而应该是内存映像之类的意思，常见的就是所加载的库。有时候可以看到这部分的 Size 大小非常大，这是没有问题的，非常正常的。比如说软件加载了显卡驱动模块，这个模块的 DLL 文件一下值就几百 MB 大小，这部分就直接被统计到 Image 部分里面，所以说这部分一般情况下不是开发人员所关注的&lt;/p&gt;

&lt;p&gt;最后一个维度就是是否在 WorkingSet 里面。在 VMMap 工具里面的 WorkingSet 被缩写为 WS 单词。简单但不准确的理解 WorkingSet 工作集就是物理内存占用量。相信大家都知道 Windows 上有可放在硬盘上的虚拟内存和放在内存条里面的物理内存的概念。可以认为 WorkingSet 就是物理内存占用量。详细的概念请看官方文档： &lt;a href=&quot;https://learn.microsoft.com/zh-cn/windows/win32/memory/working-set&quot;&gt;https://learn.microsoft.com/zh-cn/windows/win32/memory/working-set&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;在任务管理器里面常常看到的就是 WorkingSet 的占用量。但是这个占用量并不是准确的，至少可能一次 &lt;a href=&quot;https://learn.microsoft.com/zh-cn/windows/win32/api/psapi/nf-psapi-emptyworkingset&quot;&gt;EmptyWorkingSet&lt;/a&gt; 就可以将大量的在物理内存里面的内存转换到硬盘上。于是单纯靠任务管理器里面的内存占用来尝试衡量应用软件的内存使用情况是不准确的。特别是在调查 OOM 问题以及对比多个应用软件的内存使用情况时，任务管理器里面的内存占用是不具备参考意义的，看任务管理器里面的内存占用会被误导&lt;/p&gt;

&lt;p&gt;额外聊一个小知识，如果你的用户半懂不懂，想要在任务管理器里面看到的内存很低。那简单的做法就是定时调用一下 &lt;a href=&quot;https://learn.microsoft.com/zh-cn/windows/win32/api/psapi/nf-psapi-emptyworkingset&quot;&gt;EmptyWorkingSet&lt;/a&gt; 函数好了。据说 360 加速球就是这么做的&lt;/p&gt;

&lt;p&gt;常用的调查套路里面，可以使用 VMMap 工具来查看应用软件的内存使用情况，看看软件当前现场是否有内存使用异常的情况。此时需要关注点可能还需要包括碎片化问题，内存泄露问题。碎片化问题指的是尽管内存还有空间余量，但是申请内存却失败了，因为内存虽然够但是碎片化的存在，无法找到连续的内存空间。内存泄露问题指的是内存申请后没有释放，导致内存占用越来越高的问题&lt;/p&gt;

&lt;p&gt;额外说明一个知识点，对于 32 位进程来说，用户态能使用的内存就是 2 GB 空间。一旦发现应用软件已经用了接近 2GB 的内存空间，则可以认为这个软件可能已经发生了 OOM 问题了。因为配合碎片化问题的存在，接近 2GB 的内存空间就有可能存在无法申请足够的连续的内存空间而导致 OOM 问题。一旦发生了 OOM 问题，可能会导致软件进入非预期逻辑分支，如本该初始化的逻辑没有初始化，导致后续访问出现空异常等问题&lt;/p&gt;

&lt;p&gt;这些时候，配合 ProcDump 一起食用效果更好。如果通过 VMMap 工具感觉到可能是内存问题，那此时通过 ProcDump 抓一个 DUMP 文件回来分析，可能会有更多的收获&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程73.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919995430.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;通过 VMMap 不仅可以查看应用软件真实内存是怎样，还可以在用户设备上进行简单的调试。如追踪软件启动，找到是哪个模块在申请内存。因为很有可能只有在某个用户的设备上才会存在问题，那在对应的用户的设备上跑 VMMap 寻找申请大量内存的模块是非常有帮助的&lt;/p&gt;

&lt;p&gt;通过 VMMap 也可以看到是否有内存碎片化的存在，即内存空间一看就发现出现非常多的碎片化的情况&lt;/p&gt;

&lt;!-- dotMemory --&gt;
&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程74.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491999256398.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当然了，对于 dotnet 系的应用来说，别忘了还有 dotMemory 这个好用的工具。这个工具既可以在用户设备上跑起来，抓取到应用软件运行内存的 dotnet 系信息，还可以对捞回来的 DUMP 文件进行分析。这个软件的交互做的非常好，有可能大家看界面就知道如何使用了&lt;/p&gt;

&lt;p&gt;通过 dotMemory 工具可以看到各个模块的内存申请情况，各个类型存在内存的数量，以及各个类型的引用情况。适合于寻找内存泄露问题以及通过内存情况反推出软件的代码运行逻辑&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程75.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491999415134.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在聊到内存 OOM 相关问题时，一个会被开发者忽略的点是可能遇到的是爆 GDI 对象的问题。试试打开任务管理器，在选择列里开启 GDI 对象列，看看进程占用的 GDI 对象有多少&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程76.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491999539041.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;许多的 GDI 对象所占用的内存都不计入到进程里面，这就导致了进程看起来没有使用多少内存，但是系统全局的内存却是不足了。或者是进程使用了大量的 GDI 对象，导致了许多 GDI 相关函数调用失败，而通过 LastErrorCode 获取到的错误被翻译为 OOM 相关错误，进而表现出 OOM 现象&lt;/p&gt;

&lt;p&gt;此时可以通过任务管理器快速确定是否是 GDI 对象爆炸问题，然后通过 GDIView 工具查看 GDI 对象的使用情况。GDIView 工具可以帮助我们查看系统全局的 GDI 对象使用情况，以及每个进程的 GDI 对象使用情况&lt;/p&gt;

&lt;p&gt;根据 &lt;a href=&quot;https://learn.microsoft.com/zh-cn/windows/win32/direct2d/server-side-rendering-overview&quot;&gt;官方文档&lt;/a&gt; 可以知道，使用 GDI 的应用程序限制为每个进程 10240 GDI 句柄，每个会话 65536 个 GDI 句柄。 原因是 Windows 在内部使用 16 位 WORD 来存储每个会话的句柄索引&lt;/p&gt;

&lt;p&gt;GDIView 工具下载地址： &lt;a href=&quot;https://www.nirsoft.net/utils/gdi_handles.html&quot;&gt;https://www.nirsoft.net/utils/gdi_handles.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;举一个实际的案例，我的师兄写了一个程序，这个程序会不断使用 GDI 获取屏幕截图，此时获取到了大量的 Bitmap 对象，导致了系统资源大量被消耗，反过来导致可用物理内存不足，最终导致了 OOM 问题&lt;/p&gt;

&lt;p&gt;通过任务管理器看到我的师兄的程序的 GDI 对象十分多，再通过 GDIView 工具看到了大量的 Bitmap 对象，如此即可快速找到入手点。经过和师兄沟通发现可能是截图相关模块存在 GDI 对象泄露问题，反复复现和截图模块相关逻辑，看到任务管理器的 GDI 对象数量不断添加，就快速定位到了问题&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程77.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491991048352.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;以上就是本课程介绍的 OOM 问题的常用调查套路和工具。使用 VMMap 工具了解真实的内存是怎样的，避免被任务管理器误导。可在用户设备上跑 VMMap 工具了解到是哪个模块申请了大量内存，以及是否存在内存碎片化问题。使用 dotMemory 工具了解到 dotnet 系的内存使用情况，以及可能存在的内存泄露问题。如果内存没有问题，那就多看一下 GDI 对象的使用量，使用 GDIView 工具了解到 GDI 对象的使用情况&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程78.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919910142113.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;内存是一个复杂庞大的话题，本课程这里只和大家介绍了一些常见的简单情况，希望能给大家在没有调查思路的时候提供一些入手点&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程79.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919910262710.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;接下来来看看 Windows 系上特有的一个问题，注册表问题&lt;/p&gt;

&lt;!-- 注册表问题 --&gt;
&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程80.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919910477886.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;注册表问题也是一个常被 Windows 开发新手忽略的问题。有些开发者有疑惑说，我的应用代码里面明明都没有碰到注册表，为什么还可能会有注册表相关问题。其实跑在 Windows 上的程序，无论如何都会碰到注册表的。从软件进程的启动开始就在碰注册表，后续的加载 COM 组件等，都会涉及到注册表。注册表问题可能会导致软件无法启动，启动即崩溃，启动后功能不正常等问题&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程81.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919910583214.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;注册表问题可能有些深奥，本课程这里只介绍一个简单的真实案例。我发现我的 Notepad 记事本软件启动就炸掉了，不知道为什么。我通过 Process Monitor 工具抓取时，发现了其注册表有以下有趣的行为。这里就是非常靠近软件进程退出的地方&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程82.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://image.acmx.xyz/lindexi%2F20249202024222316.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;由于 Process Monitor 工具抓取注册表行为时非常刷屏，降低大家的难度，我将有问题的一行日志选择了出来，看看大家是否猜到问题&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程83.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919911229541.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;其实这个案例里面我的调查是靠经验的，这里刚好是我上回学习 COM 组件劫持时所投的毒。可以试试将注册表的各个访问项在搜索引擎里面搜索一下，看看是否有人说这个注册表项是有问题的。如果有，那就可以尝试根据网上提供的方法进一步定位问题&lt;/p&gt;

&lt;p&gt;以上案例的详细内容请参阅 &lt;a href=&quot;https://www.cnblogs.com/qianxiao996/p/13574568.html&quot;&gt;后门及持久化访问4—-Com组件劫持 - 浅笑996 - 博客园&lt;/a&gt;&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程84.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919911351885.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;相信刚才通过 Process Monitor 工具大家也感受到了被注册表刷屏的恐惧。如果应用软件的表现行为是第一次能跑，第二次就不能跑，且可能是和注册表有关的时候。此时可以尝试一下 RegistryChangesView 和 whatchanged 工具，通过这些工具可以快速帮助大家找到从事前到事后注册表的变化情况，减少刷屏的影响，提升调查效率&lt;/p&gt;

&lt;p&gt;RegistryChangesView 工具下载地址： &lt;a href=&quot;https://www.nirsoft.net/utils/registry_changes_view.html&quot;&gt;https://www.nirsoft.net/utils/registry_changes_view.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;whatchanged 工具下载地址： &lt;a href=&quot;https://www.majorgeeks.com/files/details/what_changed.html&quot;&gt;https://www.majorgeeks.com/files/details/what_changed.html&lt;/a&gt;&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程85.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919911451278.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;通过 RegistryChangesView 和 whatchanged 工具配合找到注册表变更项，再通过 Process Monitor 工具进一步辅助定位，可以了解到是哪个进程改了注册表，改了什么内容以及在什么时候改的。这样就可以帮助大家更好找到问题的入手点&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程86.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919911563978.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;注册表相关问题如果深入了解还是非常有深度的，本课程这里只介绍了一个简单的案例。希望大家能够通过这个案例了解到注册表问题的存在，以及通过 Process Monitor 工具，RegistryChangesView 和 whatchanged 工具等工具帮助大家更好的调查注册表问题&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程87.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491991268901.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;接下来看看窗口相关模块的问题&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程88.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919912181649.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;回到一开始的用户反馈的软件用不了的问题，经过咱的一番排查，发现了软件的进程没有崩溃，没有异常，没有依赖问题。捞一个 DUMP 回来也看不到问题，即无论是在 Visual Studio 里面分析还是在 WinDbg 里面分析都看不到问题。那此时就可以怀疑是窗口相关的问题了&lt;/p&gt;

&lt;p&gt;如上图所示，大家猜猜进程还在，但是用户打开的软件却啥都没有发生，可能会是什么问题？&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程89.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919912292895.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;细心的伙伴也许一眼就看到了，这个窗口的 Top 在 -1000 坐标里面。这就有趣了，如果刚好用户的屏幕分辨率不够大，那这个窗口就会被放到屏幕外面去了。此时用户打开软件，软件进程还在，但是用户看不到软件的窗口，自然就以为软件打不开用不了&lt;/p&gt;

&lt;p&gt;调查窗口相关问题时，使用少珺提供的 WindowDebugger 窗口调试神器可以调试 Win32 窗口的十分多方面的内容。这个工具可以按照多个策略搜寻窗口，如进程名、窗口句柄、窗口标题等等。搜到了窗口之后，可以看到 Win32 窗口的各个信息，如上图界面显示的窗口坐标、窗口大小、窗口标题等等。此外，还可以看到窗口的父窗口、子窗口，以及窗口的样式属性等等信息&lt;/p&gt;

&lt;p&gt;更棒的是 WindowDebugger 窗口调试神器可以更改窗口的各项属性，处理辅助调试之外，还可以用作开发工具，用于在开发侧了解窗口的各项属性，方便开发人员调试窗口相关问题&lt;/p&gt;

&lt;p&gt;WindowDebugger 窗口调试神器开源地址： &lt;a href=&quot;https://github.com/kkwpsv/WindowDebugger&quot;&gt;https://github.com/kkwpsv/WindowDebugger&lt;/a&gt;&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程90.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919912468473.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;继续回到了用户反馈的问题的这个故事上，经过了鸡飞狗跳的调试，发现了窗口之所以非到 -1000 的坐标的原因是因为有业务逻辑写了有趣的代码，判断当窗口失去焦点的时候，将窗口坐标设置到了 -1000 的位置。于是故事里的开发工程师就很疑惑了，为什么用户双击这个软件的时候，还会有其他软件来抢窗口&lt;/p&gt;

&lt;p&gt;在 Windows 上开发的时候，特别是开发全屏软件，任务栏被弹出来也是一个令人烦恼的事情。虽然有 &lt;a href=&quot;/post/WPF-%E7%A8%B3%E5%AE%9A%E7%9A%84%E5%85%A8%E5%B1%8F%E5%8C%96%E7%AA%97%E5%8F%A3%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 稳定的全屏化窗口方法&lt;/a&gt; 提供了十分稳定的方法，但是对于某些一闪而过的过来打扰的窗口，还是会降低用户的观感的&lt;/p&gt;

&lt;p&gt;故事里的开发工程师就想找到一闪而过的窗口具体是哪个窗口。由于窗口是一闪而过的，用 WindowDebugger 窗口调试神器抓不到。此时可以使用 Walterlv 开发的 Walterlv.ForegroundWindowMonitor 工具。这个工具可以实时输出焦点窗口的信息，包括窗口的标题、窗口的句柄、窗口的坐标等等。通过这个工具可以实时输出焦点窗口的信息，方便开发人员调试窗口相关问题&lt;/p&gt;

&lt;p&gt;如上图就打开了 实时输出焦点窗口工具 抓取到了是一个翻译工具一闪而过，既然知道了一闪而过的窗口，那顺着窗口提供的信息，就可以进一步调查了&lt;/p&gt;

&lt;p&gt;实时输出焦点窗口工具开源地址： &lt;a href=&quot;https://github.com/walterlv/Walterlv.ForegroundWindowMonitor&quot;&gt;https://github.com/walterlv/Walterlv.ForegroundWindowMonitor&lt;/a&gt;&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程91.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919912582544.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;用户报告的问题总不是只有一刀，故事里的开发工程师很开森卸掉了翻译工具之后，发现虽然自己的窗口起来了，能被看到了。但是似乎无法接收任何按键输入，十分奇怪&lt;/p&gt;

&lt;p&gt;故事里的开发工程师先是打开 VisualStudio 自带的 Spy++ 工具，用 Spy++ 工具抓取自己的窗口，然后监听窗口消息。发现了键盘消息都没有被自己的窗口接收到，再通过 Spy++ 爬窗口树才发现了自己在主窗口之上盖了一个窗口且让主窗口失去了焦点。于是开发工程师就很开森的优化了一顿代码，看起来一切都正常了&lt;/p&gt;

&lt;p&gt;然而有趣的是，更新之后的软件给到了用户玩了一会，用户就又来报告说过一会窗口就不见了。开发工程师经过了一顿调查发现没有异常，无论是 Visual Studio 还是 WinDbg 都抓不到任何的异常，且软件进程也还在。用 WindowDebugger 窗口调试神器却找不到进程的主窗口。看起来似乎是窗口被关闭了。用 Spy++ 抓取消息时，似乎发现窗口是被键盘消息退出的&lt;/p&gt;

&lt;p&gt;开发工程师打开了 KeyCastOW 键盘显示工具，尝试抓取一下键盘消息，结果就看到了 Alt-F4 快捷键被按下。这是一个非常通用的关闭窗口的快捷键。下一步就是找找看是不是有有趣的键盘设备、或模拟键盘设备、或某些奇怪的软件发送了键盘消息了，可以尝试拔掉各种外设以及结束各种进程，来看看是否还会有软件发送键盘消息&lt;/p&gt;

&lt;p&gt;CastOW 键盘显示工具开源地址： &lt;a href=&quot;https://github.com/brookhong/KeyCastOW&quot;&gt;https://github.com/brookhong/KeyCastOW&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;如果使用 Key CastOW 工具还是抓不到键盘消息，可以使用更狠的驱动级的 OpenArk64 工具。这个工具是驱动级的，我自己实际使用过程中发现会蓝屏，大家如果要玩这个工具的话，推荐在沙盒里面玩。OpenArk 是一款非常强大且好用的工具，我十分喜欢这个工具，也推荐大家去玩玩这个工具。在一些用户的设备上，有某些恶意广告软件会注入模块，然而这些广告软件写的不咋稳定，最终会导致进程炸掉。用 OpenArk64 在用户设备上定位模块也是一个不错的选择。使用 OpenArk64 集成的许多功能，可以在没有思路的时候这点点，那点点，也许就能找到更多线索&lt;/p&gt;

&lt;p&gt;OpenArk64 工具开源地址： &lt;a href=&quot;https://github.com/BlackINT3/OpenArk&quot;&gt;https://github.com/BlackINT3/OpenArk&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;故事的最后，开发工程师终于找到了是一个有趣的触摸屏幕在假装自己是一个键盘，且当用户触摸到触摸框的边缘的时候，自动发送 Alt-F4 键盘消息。于是开发工程师就很开心的将这个触摸屏幕砸了，软件就再也没有出现过一闪而过的窗口，也再也没有出现过窗口被关闭的问题，因为这个屏幕就再也没亮过，手动滑稽&lt;/p&gt;

&lt;p&gt;如果一个问题是纯软件层的问题，那调试难度可能就已经很高了。再加上可能存在的硬件相关的问题，那调试难度就更高了。这就是为什么开玩笑说故事里面的开发工程师要砸掉触摸屏幕的原因&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程92.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919913108638.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;以上就是本课程介绍的窗口系常用的工具，包括 WindowDebugger 窗口调试神器、Walterlv.ForegroundWindowMonitor 实时输出焦点窗口工具、KeyCastOW 键盘显示工具、OpenArk64 驱动级的键盘消息抓取工具&lt;/p&gt;

&lt;p&gt;窗口系相关问题的调试是有比较多 Win32 窗口相关前置知识点的，这部分知识需要大家平时积累&lt;/p&gt;

&lt;p&gt;且在 Win32 非常复杂的窗口属性里面，多个不同的属性的组合以及窗口之间的关系组合，可能有很多都是网上难以搜到的特殊情况。通过 WindowDebugger 窗口调试神器辅助调试，尝试开关多个不同的属性，很多时候也可以减少问题的范围面，提升调查效率&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程93.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919913217451.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;常见问题除了这些之外，还有许多专项的问题。比如网络系问题、触摸系问题、音视频系问题、DPI和DWM相关问题、数据库相关问题等等。其他专项问题都有比较明确的特征，不会说需要探索一下摸索一下，有比较特征的问题可以直接使用专项工具进行调查。本课程就不在这里展开&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程94.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024919919539190.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;以上就是本课程里面介绍的常用调查套路和工具。包括了从当下的现场出发，再到复现步骤过程中配合抓取更多信息的工具，最后到一些常见的问题的寻找入手点。但往往有很多现实遇到的问题都是十分复杂的，需要一步步进行调查，逐步深入，让我来和大家介绍一个我的终极方法&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程95.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491992046928.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;小黄鸭调试法是终极方法，当自己完全没有思路的时候，不妨找只小黄鸭聊聊天。和小黄鸭说说自己所遇到的问题，问题的表现行为，以及用了哪些调试工具，这些调试工具的结果是如何的，自己有什么猜测。很多时候，当自己和小黄鸭说完之后，自己就会有新的想法，甚至直接就找到了问题的入手点&lt;/p&gt;

&lt;p&gt;小黄鸭没有被限定为一只小黄鸭玩具，也可以是某个工具人，或者是某个人工智障。有时候找个工具人聊聊天，也会有意想不到的效果&lt;/p&gt;

&lt;p&gt;小黄鸭调试法是一个软件工程里面很有名的调试方法，详细请参阅 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%B0%8F%E9%BB%84%E9%B8%AD%E8%B0%83%E8%AF%95%E6%B3%95&quot;&gt;https://zh.wikipedia.org/wiki/%E5%B0%8F%E9%BB%84%E9%B8%AD%E8%B0%83%E8%AF%95%E6%B3%95&lt;/a&gt;&lt;/p&gt;

&lt;!-- ![](image/Windows 调试工具课程/Windows 调试工具课程96.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202491992020611.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;以上就是本次课程的内容，希望大家能够通过这次课程了解到一些常用 Windows 调试工具的使用方法，以及了解到一些常用的调试套路。希望能在实际的软件开发调试过程中有所帮助&lt;/p&gt;

&lt;p&gt;如有工具下载失败，可发邮件向我要工具&lt;/p&gt;

&lt;p&gt;更多 dotnet 系的代码调试方法请参阅： &lt;a href=&quot;/post/dotnet-%E4%BB%A3%E7%A0%81%E8%B0%83%E8%AF%95%E6%96%B9%E6%B3%95.html&quot;&gt;dotnet 代码调试方法&lt;/a&gt;
&lt;!-- [dotnet 代码调试方法-腾讯云开发者社区-腾讯云](https://cloud.tencent.com/developer/article/1446477 ) --&gt;&lt;/p&gt;

&lt;p&gt;更多技术博客，请参阅 &lt;a href=&quot;/post/%E5%8D%9A%E5%AE%A2%E5%AF%BC%E8%88%AA.html&quot;&gt;博客导航&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 07 Apr 2026 22:01:52 +0000</pubDate>
        <link>/post/Windows-%E8%B0%83%E8%AF%95%E5%B7%A5%E5%85%B7%E8%AF%BE%E7%A8%8B.html</link>
        <guid isPermaLink="true">/post/Windows-%E8%B0%83%E8%AF%95%E5%B7%A5%E5%85%B7%E8%AF%BE%E7%A8%8B.html</guid>
        
        
      </item>
    
      <item>
        <title>Vortice 使用 DirectComposition 显示透明窗口</title>
        <description>&lt;p&gt;通过 DirectComposition 配合 WS_EX_LAYERED 或 WS_EX_NOREDIRECTIONBITMAP 窗口样式，可以让窗口高性能地背景透明，完全依靠 DWM 将窗口背景和桌面画面合成&lt;/p&gt;

&lt;!--more--&gt;

&lt;!-- CreateTime:2026/02/04 07:15:58 --&gt;
&lt;!-- 发布 --&gt;
&lt;!-- 博客 --&gt;
&lt;!-- 标签：C#,D2D,DirectX,Vortice,Direct2D,渲染,DirectComposition --&gt;
&lt;!-- 置顶1 --&gt;

&lt;p&gt;本文是&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-SharpDx-%E6%B8%B2%E6%9F%93%E5%8D%9A%E5%AE%A2%E5%AF%BC%E8%88%AA.html&quot;&gt;渲染相关系列博客&lt;/a&gt;中的一篇，该系列博客已按照逻辑顺序编排，方便大家依次阅读。如您对渲染相关感兴趣，可以通过以下链接访问整个系列：&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-SharpDx-%E6%B8%B2%E6%9F%93%E5%8D%9A%E5%AE%A2%E5%AF%BC%E8%88%AA.html&quot;&gt;渲染相关系列博客导航&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;在 &lt;a href=&quot;/post/DirectX-%E4%BD%BF%E7%94%A8-Vortice-%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E6%8E%A7%E5%88%B6%E5%8F%B0%E5%88%9B%E5%BB%BA-Direct2D1-%E7%AA%97%E5%8F%A3%E4%BF%AE%E6%94%B9%E9%A2%9C%E8%89%B2.html&quot;&gt;DirectX 使用 Vortice 从零开始控制台创建 Direct2D1 窗口修改颜色&lt;/a&gt; 博客中和大家介绍了最简方式创建了窗口和对接了 DirectX 层。在此基础上，大家也能看到此时创建的窗口是无法应用透明背景效果的&lt;/p&gt;

&lt;p&gt;即使强行设置 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SwapChainDescription1.AlphaMode&lt;/code&gt; 为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AlphaMode.Premultiplied&lt;/code&gt; 也会在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IDXGIFactory2.CreateSwapChainForHwnd&lt;/code&gt; 报错：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;DXGI ERROR: IDXGIFactory::CreateSwapChain: Alpha blended swapchains must be created with CreateSwapChainForComposition, or CreateSwapChainForCoreWindow with the DXGI_SWAP_CHAIN_FLAG_FOREGROUND_LAYER flag.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;传统 Win32 应用可以通过 UpdateLayeredWindow 方法设置窗口透明，然而 UpdateLayeredWindow 是有比较大的性能代价的，详细请参阅 &lt;a href=&quot;/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;/p&gt;

&lt;p&gt;性能较好的透明窗口实现可参阅 &lt;a href=&quot;/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;

&lt;p&gt;以上是在 WPF 框架里面帮忙封装好的，现在咱只有纯控制台，需要自己手动干一些活&lt;/p&gt;

&lt;p&gt;为了方便大家阅读，本文将重新从零控制台开始，先创建好 WS_EX_LAYERED 的窗口，再将 DirectX 对接上去。总代码控制在 500 行左右。额外，为了方便 Win32 方法调用，本文还请出了 CsWin32 库，详细使用方法请参阅 
&lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-CsWin32-%E5%BA%93%E7%AE%80%E5%8C%96-Win32-%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8%E9%80%BB%E8%BE%91.html&quot;&gt;dotnet 使用 CsWin32 库简化 Win32 函数调用逻辑&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;在本文的追加部分内容，补充了 DirectComposition + WS_EX_NOREDIRECTIONBITMAP 制作带标题栏的透明窗口内容方案&lt;/p&gt;

&lt;h2 id=&quot;准备工作&quot;&gt;准备工作&lt;/h2&gt;

&lt;p&gt;按照 .NET 惯例，先安装一些库。本文的 D2D 基本没有戏份，仅用于绘制一点用于辅助测试的内容，本身此技术就和 D2D 无关&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div 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;Vortice.Direct2D1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3.8.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;Vortice.Direct3D11&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3.8.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;Vortice.DirectComposition&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3.8.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;Vortice.DXGI&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3.8.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;Vortice.Win32&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2.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;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.CsWin32&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.3.257&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;PrivateAssets&amp;gt;&lt;/span&gt;all&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PrivateAssets&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;IncludeAssets&amp;gt;&lt;/span&gt;runtime; build; native; contentfiles; analyzers&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IncludeAssets&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;/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;net10.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;Nullable&amp;gt;&lt;/span&gt;enable&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Nullable&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;IsAotCompatible&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IsAotCompatible&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PublishAot&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PublishAot&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;Vortice.Direct2D1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3.8.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;Vortice.Direct3D11&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3.8.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;Vortice.DirectComposition&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3.8.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;Vortice.DXGI&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3.8.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;Vortice.Win32&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2.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;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.CsWin32&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.3.257&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;PrivateAssets&amp;gt;&lt;/span&gt;all&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PrivateAssets&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;IncludeAssets&amp;gt;&lt;/span&gt;runtime; build; native; contentfiles; analyzers&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IncludeAssets&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;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如以上代码所示，本文提供的代码也是 AOT 友好的。在我的测试中，构建的 32 位的程序只需 2.12 MB 的体积。如果不知道本文的项目是如何组织的，可以在本文末尾找到本文全部代码的下载方法，拉取代码了解更多细节&lt;/p&gt;

&lt;p&gt;添加 NativeMethods.txt 文件，添加以下内容，让 CsWin32 辅助生成一些代码&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;EnumDisplayMonitors&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;GetMonitorInfo&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;MONITORINFOEXW&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;EnumDisplaySettings&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;GetDisplayConfigBufferSizes&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;QueryDisplayConfig&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DisplayConfigGetDeviceInfo&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DISPLAYCONFIG_SOURCE_DEVICE_NAME&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DISPLAYCONFIG_TARGET_DEVICE_NAME&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;RegisterClassEx&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;GetModuleHandle&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;LoadCursor&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IDC_ARROW&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;WndProc&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;CreateWindowEx&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;CW_USEDEFAULT&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ShowWindow&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;SW_SHOW&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;GetMessage&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;TranslateMessage&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DispatchMessage&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DefWindowProc&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;GetClientRect&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;WM&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;WM_PAINT&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;GetWindowLong&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;SetWindowLong&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DwmIsCompositionEnabled&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;UpdateLayeredWindow&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DwmExtendFrameIntoClientArea&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DCompositionCreateDevice&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上提供的列表是超过本文所用范围的，多了也没有什么关系，一来这是测试项目，二来发布的时候 AOT 带裁剪&lt;/p&gt;

&lt;h2 id=&quot;创建窗口&quot;&gt;创建窗口&lt;/h2&gt;

&lt;p&gt;创建窗口的步骤和 &lt;a href=&quot;/post/DirectX-%E4%BD%BF%E7%94%A8-Vortice-%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E6%8E%A7%E5%88%B6%E5%8F%B0%E5%88%9B%E5%BB%BA-Direct2D1-%E7%AA%97%E5%8F%A3%E4%BF%AE%E6%94%B9%E9%A2%9C%E8%89%B2.html&quot;&gt;上一篇博客&lt;/a&gt; 提供的方法十分接近，只是需要配置 WS_EX_LAYERED 样式，核心代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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_EX_STYLE&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;WINDOW_EX_STYLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WS_EX_OVERLAPPEDWINDOW&lt;/span&gt;
                                  &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WINDOW_EX_STYLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WS_EX_LAYERED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Layered 是透明窗口的关键（但即使没有设置也没关系）&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;style&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WNDCLASS_STYLES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CS_OWNDC&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WNDCLASS_STYLES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CS_HREDRAW&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WNDCLASS_STYLES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CS_VREDRAW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;defaultCursor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LoadCursor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HINSTANCE&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;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PCWSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDC_ARROW&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;className&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;lindexi-&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;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;title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;The Title&quot;&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;pClassName&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;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;pTitle&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;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wndClassEx&lt;/span&gt; &lt;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;WNDCLASSEXW&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;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;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SizeOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WNDCLASSEXW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;style&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;lpfnWndProc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&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;WndProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 注： 这里没有作为字段，可能会被 GC 回收，请不要在实际项目这么编写&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;hInstance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HINSTANCE&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;nf&quot;&gt;DangerousGetHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;hCursor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;defaultCursor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;hbrBackground&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HBRUSH&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;lpszClassName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PCWSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pClassName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;ushort&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;atom&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RegisterClassEx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wndClassEx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dwStyle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WINDOW_STYLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WS_OVERLAPPEDWINDOW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;windowHwnd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateWindowEx&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;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PCWSTR&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;atom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PCWSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pTitle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;dwStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;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;1900&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;HWND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HMENU&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Null&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;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;windowHwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上代码放在 CreateWindow 方法中。在开始之前，也先调用 DwmIsCompositionEnabled 方法，判断是否可用&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;DwmIsCompositionEnabled&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;compositionEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compositionEnabled&lt;/span&gt;&lt;span class=&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;p&gt;预期在 Win10 以上系统都是能使用的，除非系统被魔改&lt;/p&gt;

&lt;p&gt;窗口的消息处理代码 WndProc 先不着急写，等待完成渲染部分的逻辑再一起写&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;window&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateWindow&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;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SHOW_WINDOW_CMD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SW_NORMAL&lt;/span&gt;&lt;span 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;渲染线程相关逻辑，我封装到 RenderManager 类型里面，其代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;renderManager&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RenderManager&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;renderManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StartRenderThread&lt;/span&gt;&lt;span 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;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;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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getMessageResult&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;(&amp;amp;&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;HWND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;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;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getMessageResult&lt;/span&gt;&lt;span class=&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;nf&quot;&gt;TranslateMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&amp;amp;&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;(&amp;amp;&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;在 RenderManager 里面也提供窗口尺寸变更的方法，可以在消息循环中调用。此时的消息循环的核心代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;LRESULT&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;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;uint&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;WPARAM&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;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;WindowsMessage&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;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WindowsMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WM_NCCALCSIZE&lt;/span&gt;&lt;span class=&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;LRESULT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;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;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WindowsMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WM_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;RenderManager&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;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;nf&quot;&gt;DefWindowProc&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;message&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上的 WM_NCCALCSIZE 用于声明客户区，通过直接返回 0 告诉系统整个区域都是客户区。否则将出现标题栏不可见，但是点击有效，能拖动窗口也能在原本的最小化、最大化、关闭窗口按钮所在的位置点击响应对应的功能&lt;/p&gt;

&lt;p&gt;透明窗口的实现在窗口创建过程中，最关键点只有 WS_EX_LAYERED 和 WM_NCCALCSIZE 的逻辑 &lt;del&gt;。其中 WM_NCCALCSIZE 最为关键。不带 WS_EX_LAYERED 只会出现边框而已&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;以上为采用 WS_EX_LAYERED 的方式，此方法没有采用 WS_EX_NOREDIRECTIONBITMAP 来的高效。如 &lt;a href=&quot;/post/Windows-%E7%AA%97%E5%8F%A3%E6%A0%B7%E5%BC%8F-%E4%BB%80%E4%B9%88%E6%98%AF-WS_EX_NOREDIRECTIONBITMAP-%E6%A0%B7%E5%BC%8F.html&quot;&gt;Windows 窗口样式 什么是 WS_EX_NOREDIRECTIONBITMAP 样式&lt;/a&gt; 博客所述，可以知道，对比采用 WS_EX_LAYERED 分层窗口的方式需要由 CPU 处理分层窗口导致的性能损耗（不一定，详见注），采用 WS_EX_NOREDIRECTIONBITMAP 方式配合 DirectComposition 可以实现更加高性能的窗口透明效果，直接将 DirectComposition 的像素交给 DWM 合成，可让全过程发生在 GPU 中，无 CPU-GPU 的拷贝损耗和带宽占用&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;注： 从 Windows 8 和 8.1 开始，虽然 User32 一直没有发生显著变化，但细微变化也有，即完全支持 GPU 上的每像素 alpha 值混合处理，且将窗口表面传输到系统内存的费用也取消了。也就是说，如果我不需要执行每像素命中测试，现在就能够生成分层窗口效果，同时不会对性能造成影响
https://learn.microsoft.com/zh-cn/archive/msdn-magazine/2014/june/windows-with-c-high-performance-window-layering-using-the-windows-composition-engine&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;由于本文在编写的时候，本金鱼忘记了 WS_EX_NOREDIRECTIONBITMAP 配置，后面想加上时，担心破坏文章的连贯性，于是将 WS_EX_NOREDIRECTIONBITMAP 部分安排在本文末尾的追加内容中&lt;/p&gt;

&lt;h2 id=&quot;渲染线程&quot;&gt;渲染线程&lt;/h2&gt;

&lt;p&gt;独立的渲染线程也是 WPF 等 UI 框架所采用的方式，只需要新建一个线程跑起来就可以了，如果有心的话，再设置线程为 STA 的就更好，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;unsafe&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RenderManager&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;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&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;=&amp;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;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Format&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_colorFormat&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;B8G8R8A8_UNorm&lt;/span&gt;&lt;span class=&quot;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;StartRenderThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;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;nf&quot;&gt;RenderCore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&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;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;Render&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;n&quot;&gt;Priority&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ThreadPriority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Highest&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上的 RenderCore 就是核心的渲染方法了&lt;/p&gt;

&lt;p&gt;由于渲染线程是独立的，不能在 ReSize 方法直接修改渲染线程相关的逻辑。本文这里简单使用一个字段表示窗口尺寸变更，需要渲染线程修改交换链尺寸&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;unsafe&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RenderManager&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;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;ReSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_isReSize&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;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_isReSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在 RenderCore 的一开始就是执行初始化逻辑，初始化为本文的关键，核心就是对接 DirectComposition 实现透明窗口效果&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;RECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;windowRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;GetClientRect&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;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;windowRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientSize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SizeI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;windowRect&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;windowRect&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;windowRect&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;windowRect&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;按照  &lt;a href=&quot;/post/DirectX-%E4%BD%BF%E7%94%A8-Vortice-%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E6%8E%A7%E5%88%B6%E5%8F%B0%E5%88%9B%E5%BB%BA-Direct2D1-%E7%AA%97%E5%8F%A3%E4%BF%AE%E6%94%B9%E9%A2%9C%E8%89%B2.html&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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dxgiFactory2&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;CreateDXGIFactory1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDXGIFactory2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;IDXGIAdapter1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hardwareAdapter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetHardwareAdapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dxgiFactory2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 这里 ToList 只是想列出所有的 IDXGIAdapter1 在实际代码里，大部分都是获取第一个&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;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;hardwareAdapter&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;Cannot detect D3D11 adapter&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;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;IDXGIAdapter1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetHardwareAdapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDXGIFactory2&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;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;IDXGIFactory6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;factory6&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;n&quot;&gt;QueryInterfaceOrNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDXGIFactory6&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;factory6&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;// 这个系统的 DX 支持 IDXGIFactory6 类型&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;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;adapterIndex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;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;factory6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EnumAdapterByGpuPreference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;adapterIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GpuPreference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HighPerformance&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;IDXGIAdapter1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;adapter&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;n&quot;&gt;adapterIndex&lt;/span&gt;&lt;span class=&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;adapter&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;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;n&quot;&gt;AdapterDescription1&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;adapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Description1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&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;Flags&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AdapterFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Software&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AdapterFlags&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;c1&quot;&gt;// Don&apos;t select the Basic Render Driver adapter.&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;adapter&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;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;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;adapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Description1&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;&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;adapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&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;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;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;adapterIndex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;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;factory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EnumAdapters1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;adapterIndex&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;IDXGIAdapter1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;adapter&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;n&quot;&gt;adapterIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;AdapterDescription1&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;adapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Description1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&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;Flags&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AdapterFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Software&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AdapterFlags&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;c1&quot;&gt;// Don&apos;t select the Basic Render Driver adapter.&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;adapter&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;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;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;adapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Description1&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;&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;adapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;尝试创建 ID3D11Device 设备，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;FeatureLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;featureLevels&lt;/span&gt; &lt;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;FeatureLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Level_12_2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;FeatureLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Level_12_1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;FeatureLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Level_12_0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;FeatureLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Level_11_1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;FeatureLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Level_11_0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;FeatureLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Level_10_1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;FeatureLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Level_10_0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;FeatureLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Level_9_3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;FeatureLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Level_9_2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;FeatureLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Level_9_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;IDXGIAdapter1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;adapter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hardwareAdapter&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;n&quot;&gt;creationFlags&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;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;D3D11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;D3D11CreateDevice&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;adapter&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;Unknown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;creationFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;featureLevels&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;ID3D11Device&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d3D11Device&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;FeatureLevel&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;featureLevel&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;ID3D11DeviceContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d3D11DeviceContext&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;本文以上的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FeatureLevel[]&lt;/code&gt; 中添加了 Level_12_2 等的不合理需求，如果创建失败了，则执行降级逻辑。按照技术原理，只需有 Level_11_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;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;Failure&lt;/span&gt;&lt;span class=&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;featureLevels&lt;/span&gt; &lt;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;c1&quot;&gt;//FeatureLevel.Level_12_2,&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;//FeatureLevel.Level_12_1,&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;//FeatureLevel.Level_12_0,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;FeatureLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Level_11_1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;FeatureLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Level_11_0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;FeatureLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Level_10_1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;FeatureLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Level_10_0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;FeatureLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Level_9_3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;FeatureLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Level_9_2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;FeatureLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Level_9_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;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;D3D11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;D3D11CreateDevice&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;adapter&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;Unknown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;creationFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;featureLevels&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;d3D11Device&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;featureLevel&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;d3D11DeviceContext&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;将获取到的 ID3D11Device 当成 ID3D11Device1 设备来用，这一步基本不会遇到出错的，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;// 大部分情况下，用的是 ID3D11Device1 和 ID3D11DeviceContext1 类型&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 从 ID3D11Device 转换为 ID3D11Device1 类型&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ID3D11Device1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d3D11Device1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d3D11Device&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;ID3D11Device1&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;d3D11DeviceContext1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d3D11DeviceContext&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;ID3D11DeviceContext1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 获取到了新的两个接口，就可以减少 `d3D11Device` 和 `d3D11DeviceContext` 的引用计数。调用 Dispose 不会释放掉刚才申请的 D3D 资源，只是减少引用计数&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;d3D11Device&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;d3D11DeviceContext&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;/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;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;FrameCount&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;SwapChainDescription1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;swapChainDescription&lt;/span&gt; &lt;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;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;uint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientSize&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;kt&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientSize&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;_colorFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;BufferCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrameCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;BufferUsage&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;RenderTargetOutput&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;n&quot;&gt;SampleDescription&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;Scaling&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;Stretch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;SwapEffect&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SwapEffect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FlipSequential&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 使用 FlipSequential 配合 Composition&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;AlphaMode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AlphaMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Premultiplied&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;SwapChainFlags&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;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Scaling： Scaling.Stretch&lt;/li&gt;
  &lt;li&gt;SwapEffect： SwapEffect.FlipDiscard 或 SwapEffect.FlipSequential ，正常来说都会采用 FlipSequential 配合 Composition 使用&lt;/li&gt;
  &lt;li&gt;AlphaMode： AlphaMode.Premultiplied 。使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AlphaMode.Ignore&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AlphaMode.Unspecified&lt;/code&gt; 参数也是合法的，但是如此就丢失了窗口透明了，不是咱的需求。而 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AlphaMode.Straight&lt;/code&gt; 参数则是不搭的&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果以上参数不搭配，则会在创建交换链时，返回 0x887A0001 错误&lt;/p&gt;

&lt;p&gt;上文提到了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AlphaMode.Premultiplied&lt;/code&gt; 预乘，简单来说就是最终输出的值里的 RGB 分量都乘以透明度。更多细节请参阅 &lt;a href=&quot;https://learn.microsoft.com/zh-cn/windows/win32/direct2d/supported-pixel-formats-and-alpha-modes#about-premultiplied-and-straight-alpha-modes&quot;&gt;支持的像素格式和 Alpha 模式 - Win32 apps - Microsoft Learn&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;判断系统版本，决定能否使用 DirectComposition 功能，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;// 使用 DirectComposition 才能支持透明窗口&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;useDirectComposition&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;// 使用 DirectComposition 时有系统版本要求&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;useDirectComposition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;useDirectComposition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OperatingSystem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsWindowsVersionAtLeast&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;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;p&gt;如此可以让代码走两个分支，使用 DirectComposition 的分支的代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;IDXGISwapChain1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;swapChain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;useDirectComposition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 使用 CreateSwapChainForComposition 创建支持预乘 Alpha 的 SwapChain&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;swapChain&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;dxgiFactory2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateSwapChainForComposition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d3D11Device1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;swapChainDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 创建 DirectComposition 设备和目标&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;IDXGIDevice&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dxgiDevice&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d3D11Device1&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;IDXGIDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;IDCompositionDevice&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compositionDevice&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DComp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DCompositionCreateDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDCompositionDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dxgiDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;compositionDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateTargetForHwnd&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;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDCompositionTarget&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;c1&quot;&gt;// 创建视觉对象并设置 SwapChain 作为内容&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;IDCompositionVisual&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compositionVisual&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compositionDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;compositionVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;swapChain&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;nf&quot;&gt;SetRoot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compositionVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;compositionDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Commit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;从上面代码可见核心步骤是先让 CreateSwapChainForComposition 创建出交换链对象。再将 ID3D11Device1 当成 IDXGIDevice 设备，用于调用 DComp.DCompositionCreateDevice 创建出 IDCompositionDevice 设备&lt;/p&gt;

&lt;p&gt;调用 IDCompositionDevice 的 CreateTargetForHwnd 方法即可为当前的窗口挂上 IDCompositionTarget 对象。随后再调用 IDCompositionDevice 设备的 CreateVisual 创建 IDCompositionVisual 视觉对象。将刚才创建出来的交换链作为视觉对象的内容，如此即可完成交换链与内容的绑定&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;// 创建视觉对象并设置 SwapChain 作为内容&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;IDCompositionVisual&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compositionVisual&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compositionDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;compositionVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;swapChain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在交换链所渲染的画面已经能够到 IDCompositionVisual 里了，再将 IDCompositionVisual 作为 IDCompositionTarget 的根，即可让 IDCompositionVisual 参与 DWM 合成&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;compositionTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetRoot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compositionVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;compositionDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Commit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果没有 DirectComposition 可用，则依然使用上一篇博客介绍的方法创建交换链，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;IDXGISwapChain1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;swapChain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;useDirectComposition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&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;fullscreenDescription&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SwapChainFullscreenDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Windowed&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;// 比如设置为忽略，否则将会报错。错误信息如下，只有 CreateSwapChainForComposition 或 CreateSwapChainForCoreWindow 才能使用&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// DXGI ERROR: IDXGIFactory::CreateSwapChain: Alpha blended swapchains must be created with CreateSwapChainForComposition, or CreateSwapChainForCoreWindow with the DXGI_SWAP_CHAIN_FLAG_FOREGROUND_LAYER flag.&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;swapChainDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AlphaMode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AlphaMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ignore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;swapChain&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dxgiFactory2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateSwapChainForHwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d3D11Device1&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;swapChainDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;fullscreenDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上代码的 DirectComposition 为本文的核心，只需要创建出输出带预乘的交换链，配合 WS_EX_LAYERED 窗口，即可渲染出透明窗口&lt;/p&gt;

&lt;p&gt;接下来的逻辑就是和 D2D 对接，尝试渲染透明的界面用于测试&lt;/p&gt;

&lt;h2 id=&quot;对接渲染&quot;&gt;对接渲染&lt;/h2&gt;

&lt;p&gt;由于 D2D 没有什么戏份，本文就只贴出核心代码&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;D2D.ID2D1Factory1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d2DFactory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;D2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;D2D1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;D2D1CreateFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;D2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID2D1Factory1&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;d3D11Texture2D&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_renderContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SwapChain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetBuffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID3D11Texture2D&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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dxgiSurface&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d3D11Texture2D&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;IDXGISurface&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;renderTargetProperties&lt;/span&gt; &lt;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;D2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RenderTargetProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&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;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PixelFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;D2DColorFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Vortice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DCommon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AlphaMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Premultiplied&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;D2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RenderTargetType&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;p&quot;&gt;};&lt;/span&gt;

                &lt;span class=&quot;n&quot;&gt;D2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID2D1RenderTarget&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d2D1RenderTarget&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;d2DFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateDxgiSurfaceRenderTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dxgiSurface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;renderTargetProperties&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;_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;D2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID2D1RenderTarget&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;renderTarget&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d2D1RenderTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

                &lt;span class=&quot;n&quot;&gt;renderTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;BeginDraw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

                &lt;span class=&quot;kt&quot;&gt;var&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;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Color4&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;Shared&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NextSingle&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;Shared&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NextSingle&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;Shared&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NextSingle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.1f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;renderTarget&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;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                &lt;span class=&quot;n&quot;&gt;renderTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EndDraw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

                &lt;span class=&quot;n&quot;&gt;_renderContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SwapChain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Present&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;PresentFlags&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;_renderContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;D3D11DeviceContext1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Flush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果准备处理窗口尺寸改变，则需要在循环里面判断 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_isReSize&lt;/code&gt; 字段，调用交换链的 ResizeBuffers 方法，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;_isReSize&lt;/span&gt;&lt;span class=&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;_isReSize&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;GetClientRect&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;pClientRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientSize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SizeI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pClientRect&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;pClientRect&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;pClientRect&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;pClientRect&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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;swapChain&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_renderContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SwapChain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

                &lt;span class=&quot;n&quot;&gt;swapChain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ResizeBuffers&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;uint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clientSize&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;uint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clientSize&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;_colorFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;SwapChainFlags&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;/p&gt;

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

&lt;p&gt;本文代码放在 &lt;a href=&quot;https://github.com/lindexi/lindexi_gd/tree/369de6b65c4122cec6a6c9ffbcc0b352a419e83e/DirectX/D2D/FarjairyakaBurnefuwache&quot;&gt;github&lt;/a&gt; 和 &lt;a href=&quot;https://gitee.com/lindexi/lindexi_gd/tree/369de6b65c4122cec6a6c9ffbcc0b352a419e83e/DirectX/D2D/FarjairyakaBurnefuwache&quot;&gt;gitee&lt;/a&gt; 上，可以使用如下命令行拉取代码。我整个代码仓库比较庞大，使用以下命令行可以进行部分拉取，拉取速度比较快&lt;/p&gt;

&lt;p&gt;先创建一个空文件夹，接着使用命令行 cd 命令进入此空文件夹，在命令行里面输入以下代码，即可获取到本文的代码&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;git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 369de6b65c4122cec6a6c9ffbcc0b352a419e83e
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上使用的是国内的 gitee 的源，如果 gitee 不能访问，请替换为 github 的源。请在命令行继续输入以下代码，将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码，可以发邮件向我要代码&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;git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 369de6b65c4122cec6a6c9ffbcc0b352a419e83e
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;获取代码之后，进入 DirectX/D2D/FarjairyakaBurnefuwache 文件夹，即可获取到源代码&lt;/p&gt;

&lt;p&gt;更多技术博客，请参阅 &lt;a href=&quot;/post/%E5%8D%9A%E5%AE%A2%E5%AF%BC%E8%88%AA.html&quot;&gt;博客导航&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;采用-ws_ex_noredirectionbitmap-的方案&quot;&gt;采用 WS_EX_NOREDIRECTIONBITMAP 的方案&lt;/h2&gt;

&lt;p&gt;上文提及的是采用 WS_EX_LAYERED 分层窗口的方式，此方式与采用 WS_EX_NOREDIRECTIONBITMAP 的方案的代码写法非常接近，只是 WS_EX_NOREDIRECTIONBITMAP 可以带来更高的性能&lt;/p&gt;

&lt;p&gt;其改动全在 CreateWindow 中，只需将 WINDOW_EX_STYLE 从 WS_EX_LAYERED 换成 WS_EX_NOREDIRECTIONBITMAP 即可&lt;/p&gt;

&lt;p&gt;在使用 WS_EX_NOREDIRECTIONBITMAP 中，还可以在 WndProc 干掉 WM_NCCALCSIZE 用来保留窗口边框，即窗口标题栏&lt;/p&gt;

&lt;p&gt;修改之后的 CreateWindow 方法中的 WINDOW_EX_STYLE 配置如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;HWND&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;DwmIsCompositionEnabled&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;compositionEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compositionEnabled&lt;/span&gt;&lt;span class=&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;c1&quot;&gt;// [Windows 窗口样式 什么是 WS_EX_NOREDIRECTIONBITMAP 样式](/post/Windows-%E7%AA%97%E5%8F%A3%E6%A0%B7%E5%BC%8F-%E4%BB%80%E4%B9%88%E6%98%AF-WS_EX_NOREDIRECTIONBITMAP-%E6%A0%B7%E5%BC%8F.html )&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;WINDOW_EX_STYLE&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;WINDOW_EX_STYLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WS_EX_NOREDIRECTIONBITMAP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;style&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WNDCLASS_STYLES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CS_OWNDC&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WNDCLASS_STYLES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CS_HREDRAW&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WNDCLASS_STYLES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CS_VREDRAW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;defaultCursor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LoadCursor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HINSTANCE&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;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PCWSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDC_ARROW&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;className&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;lindexi-&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;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;title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;The Title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_myInstanceWndProc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&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;WndProc&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;pClassName&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;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;pTitle&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;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wndClassEx&lt;/span&gt; &lt;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;WNDCLASSEXW&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;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;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SizeOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WNDCLASSEXW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;style&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;lpfnWndProc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_myInstanceWndProc&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;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HINSTANCE&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;nf&quot;&gt;DangerousGetHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;hCursor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;defaultCursor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;hbrBackground&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HBRUSH&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;lpszClassName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PCWSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pClassName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;ushort&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;atom&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RegisterClassEx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wndClassEx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dwStyle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WINDOW_STYLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WS_OVERLAPPEDWINDOW&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WINDOW_STYLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WS_VISIBLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;windowHwnd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateWindowEx&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;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PCWSTR&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;atom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PCWSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pTitle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;dwStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;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;1900&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;HWND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HMENU&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Null&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;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;windowHwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&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;_myInstanceWndProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可见核心关键改动只有 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WINDOW_EX_STYLE exStyle = WINDOW_EX_STYLE.WS_EX_NOREDIRECTIONBITMAP;&lt;/code&gt; 这一行&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;注： 原博客代码里面，是直接使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;new WNDPROC(WndProc)&lt;/code&gt; 局部变量的写法，会导致当委托被回收的时候，抛出如下异常:&lt;/p&gt;
&lt;/blockquote&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;Process terminated.
A callback was made on a garbage collected delegate of type &apos;NearajurkeekallnoYabarfoge!Windows.Win32.UI.WindowsAndMessaging.WNDPROC::Invoke&apos;.
   at Windows.Win32.PInvoke.GetMessage(Windows.Win32.UI.WindowsAndMessaging.MSG*, Windows.Win32.Foundation.HWND, UInt32, UInt32)
   at NearajurkeekallnoYabarfoge.DemoWindow.Run()
   at NearajurkeekallnoYabarfoge.Program.Main(System.String[])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;解决方法就是将委托放在字段里面，防止被回收。详细请看： &lt;a href=&quot;https://github.com/lindexi/lindexi_gd/pull/85&quot;&gt;https://github.com/lindexi/lindexi_gd/pull/85&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;如果期望显示标题栏，则可以继续在 WndProc 进行改动，删除 WM_NCCALCSIZE 相关代码，删除之后的代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;LRESULT&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;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;uint&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;WPARAM&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;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;WindowsMessage&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;WindowsMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WM_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;_renderManager&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;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DefWindowProc&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;message&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;更改使用 WS_EX_NOREDIRECTIONBITMAP 的代码放在 &lt;a href=&quot;https://github.com/lindexi/lindexi_gd/tree/5b79f1c45819750fa9e931d78b3797a81c877294/DirectX/D2D/NearajurkeekallnoYabarfoge&quot;&gt;github&lt;/a&gt; 和 &lt;a href=&quot;https://gitee.com/lindexi/lindexi_gd/tree/5b79f1c45819750fa9e931d78b3797a81c877294/DirectX/D2D/NearajurkeekallnoYabarfoge&quot;&gt;gitee&lt;/a&gt; 上，可以使用如下命令行拉取代码。我整个代码仓库比较庞大，使用以下命令行可以进行部分拉取，拉取速度比较快&lt;/p&gt;

&lt;p&gt;先创建一个空文件夹，接着使用命令行 cd 命令进入此空文件夹，在命令行里面输入以下代码，即可获取到本文的代码&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;git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 5b79f1c45819750fa9e931d78b3797a81c877294
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上使用的是国内的 gitee 的源，如果 gitee 不能访问，请替换为 github 的源。请在命令行继续输入以下代码，将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码，可以发邮件向我要代码&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;git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 5b79f1c45819750fa9e931d78b3797a81c877294
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;获取代码之后，进入 DirectX/D2D/NearajurkeekallnoYabarfoge 文件夹，即可获取到源代码&lt;/p&gt;

&lt;p&gt;此方案即可制作出保留窗口标题，窗口内容透明可与窗口背后的内容做 Alpha 透明合成叠加的窗口效果&lt;/p&gt;

&lt;h2 id=&quot;性能提醒&quot;&gt;性能提醒&lt;/h2&gt;

&lt;p&gt;无论是采用 WS_EX_LAYERED 还是 WS_EX_NOREDIRECTIONBITMAP 的方案，都会导致 DWM 需要更多的性能损耗&lt;/p&gt;

&lt;p&gt;如果自己的应用是没有透明背景需求的，建议不要带上这两个选项以降低 DWM 合成的损耗。或依然保持窗口样式，但在交换链配置 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;swapChainDescription.AlphaMode = AlphaMode.Ignore;&lt;/code&gt; 也能减少 DWM 的损耗&lt;/p&gt;

&lt;p&gt;只有在 4k 全屏应用里面，才能比较明显看到开启透明窗口带来的性能损耗&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;ul&gt;
  &lt;li&gt;屏幕： 3840x2160 (4K) + 百分百 DPI&lt;/li&gt;
  &lt;li&gt;CPU： i5-12450H&lt;/li&gt;
  &lt;li&gt;GPU： 集显&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;测试结果如下：&lt;/p&gt;

&lt;h4 id=&quot;directcompositionpremultipliedalphamode&quot;&gt;DirectCompositionPremultipliedAlphaMode&lt;/h4&gt;

&lt;p&gt;采用 DirectComposition 和配置交换链的 AlphaMode 为 Premultiplied 预乘，此为本文的演示透明窗口组合效果。代码片段如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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_EX_STYLE&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;WINDOW_EX_STYLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WS_EX_NOREDIRECTIONBITMAP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;SwapChainDescription1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;swapChainDescription&lt;/span&gt; &lt;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;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;uint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientSize&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;kt&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientSize&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;_colorFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;BufferCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FrameCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;BufferUsage&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;RenderTargetOutput&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;n&quot;&gt;SampleDescription&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;Scaling&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;Stretch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;SwapEffect&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SwapEffect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FlipSequential&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 使用 FlipSequential 配合 Composition&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;AlphaMode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AlphaMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Premultiplied&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;SwapChainFlags&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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;swapChain&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dxgiFactory2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateSwapChainForComposition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d3D11Device1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;swapChainDescription&lt;/span&gt;&lt;span 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;CPU： 忽略不计&lt;/li&gt;
  &lt;li&gt;GPU： 占用 75-80&lt;/li&gt;
  &lt;li&gt;DWM： 占用 65-70&lt;/li&gt;
  &lt;li&gt;满帧率&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;可见此时会让 DWM 非常繁忙。整体的 GPU 占用比较高&lt;/p&gt;

&lt;h4 id=&quot;directcompositionwithws_ex_layered&quot;&gt;DirectCompositionWithWS_EX_LAYERED&lt;/h4&gt;

&lt;p&gt;和 DirectCompositionPremultipliedAlphaMode 不同的是，仅将窗口样式从 WS_EX_NOREDIRECTIONBITMAP 换成 WS_EX_LAYERED 而已，代码差异如下&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;-       WINDOW_EX_STYLE exStyle = WINDOW_EX_STYLE.WS_EX_NOREDIRECTIONBITMAP;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+       WINDOW_EX_STYLE exStyle = WINDOW_EX_STYLE.WS_EX_LAYERED;
&lt;/span&gt;
        SwapChainDescription1 swapChainDescription = new()
        {
            Width = (uint) clientSize.Width,
            Height = (uint) clientSize.Height,
            Format = _colorFormat,
            BufferCount = FrameCount,
            BufferUsage = Usage.RenderTargetOutput,
            SampleDescription = SampleDescription.Default,
            Scaling = Scaling.Stretch,
            SwapEffect = SwapEffect.FlipSequential, // 使用 FlipSequential 配合 Composition
            AlphaMode = AlphaMode.Premultiplied,
            Flags = SwapChainFlags.None,
        };

        var swapChain = dxgiFactory2.CreateSwapChainForComposition(d3D11Device1, swapChainDescription);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

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

&lt;ul&gt;
  &lt;li&gt;CPU： 忽略不计&lt;/li&gt;
  &lt;li&gt;GPU： 占用 77-85&lt;/li&gt;
  &lt;li&gt;DWM： 占用 75-80&lt;/li&gt;
  &lt;li&gt;满帧率&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;可见采用 WS_EX_LAYERED 样式基本和 WS_EX_NOREDIRECTIONBITMAP 持平，差异几乎测试不到，符合文档说明内容&lt;/p&gt;

&lt;h4 id=&quot;directcompositionignorealphamode&quot;&gt;DirectCompositionIgnoreAlphaMode&lt;/h4&gt;

&lt;p&gt;和 DirectCompositionPremultipliedAlphaMode 不同的是，仅将 AlphaMode 设置为忽略，此时将丢失窗口透明效果&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;        WINDOW_EX_STYLE exStyle = WINDOW_EX_STYLE.WS_EX_NOREDIRECTIONBITMAP;

-       swapChainDescription.AlphaMode = AlphaMode.Premultiplied;
&lt;span class=&quot;gi&quot;&gt;+       swapChainDescription.AlphaMode = AlphaMode.Ignore;
&lt;/span&gt;
        var swapChain = dxgiFactory2.CreateSwapChainForComposition(d3D11Device1, swapChainDescription);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

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

&lt;ul&gt;
  &lt;li&gt;CPU： 忽略不计&lt;/li&gt;
  &lt;li&gt;GPU： 占用 5&lt;/li&gt;
  &lt;li&gt;DWM： 0-1&lt;/li&gt;
  &lt;li&gt;满帧率&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;此时的窗口没有透明效果，聪明的 DWM 也就没有需要执行合成逻辑，自然占用也就少了&lt;/p&gt;

&lt;h4 id=&quot;directcompositionwithoutws_ex_noredirectionbitmap&quot;&gt;DirectCompositionWithoutWS_EX_NOREDIRECTIONBITMAP&lt;/h4&gt;

&lt;p&gt;和 DirectCompositionPremultipliedAlphaMode 不同的是，去掉了窗口的 WS_EX_NOREDIRECTIONBITMAP 样式，此时也会丢失窗口的透明效果，且这个组合是不正确的，正常不会有人会这么做&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;-        WINDOW_EX_STYLE exStyle = WINDOW_EX_STYLE.WS_EX_NOREDIRECTIONBITMAP;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+        WINDOW_EX_STYLE exStyle = default;
&lt;/span&gt;
        swapChainDescription.AlphaMode = AlphaMode.Premultiplied;

        var swapChain = dxgiFactory2.CreateSwapChainForComposition(d3D11Device1, swapChainDescription);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

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

&lt;ul&gt;
  &lt;li&gt;CPU： 忽略不计&lt;/li&gt;
  &lt;li&gt;GPU： 占用 45-50&lt;/li&gt;
  &lt;li&gt;DWM： 40&lt;/li&gt;
  &lt;li&gt;满帧率&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这个组合比较奇怪，跑出来的效果也比较奇怪。我感觉是因为此时需要重定向表面，导致存在一些带宽损耗&lt;/p&gt;

&lt;h4 id=&quot;createswapchainforhwnd&quot;&gt;CreateSwapChainForHwnd&lt;/h4&gt;

&lt;p&gt;直接使用 CreateSwapChainForHwnd 传统方式，不再使用 DirectComposition 创建，丢失窗口透明效果，代码差异如下&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;        WINDOW_EX_STYLE exStyle = WINDOW_EX_STYLE.WS_EX_NOREDIRECTIONBITMAP;

-       swapChainDescription.AlphaMode = AlphaMode.Premultiplied;
&lt;span class=&quot;gi&quot;&gt;+       swapChainDescription.AlphaMode = AlphaMode.Ignore;
&lt;/span&gt;
-       var swapChain = dxgiFactory2.CreateSwapChainForComposition(d3D11Device1, swapChainDescription);
&lt;span class=&quot;gi&quot;&gt;+       var swapChain = dxgiFactory2.CreateSwapChainForHwnd(d3D11Device1, HWND, swapChainDescription, fullscreenDescription);        
&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;CPU： 忽略不计&lt;/li&gt;
  &lt;li&gt;GPU： 占用 5&lt;/li&gt;
  &lt;li&gt;DWM： 0-1&lt;/li&gt;
  &lt;li&gt;满帧率&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;此时可见使用传统的 CreateSwapChainForHwnd 方式和 DirectComposition 忽略 AlphaMode 的性能持平，此时两者都能达到较好的效果&lt;/p&gt;

&lt;p&gt;无论是对 CreateSwapChainForHwnd 设置的 WINDOW_EX_STYLE 为无或 WS_EX_LAYERED 都对此结果没有影响&lt;/p&gt;

&lt;h3 id=&quot;性能测试代码&quot;&gt;性能测试代码&lt;/h3&gt;

&lt;p&gt;以上代码放在 &lt;a href=&quot;https://github.com/lindexi/lindexi_gd/tree/b678a0cc6d689aa63fd1239bb88113ff8a4b9fcd/DirectX/D2D/LurqificelgallRikurneawekearner&quot;&gt;github&lt;/a&gt; 和 &lt;a href=&quot;https://gitee.com/lindexi/lindexi_gd/tree/b678a0cc6d689aa63fd1239bb88113ff8a4b9fcd/DirectX/D2D/LurqificelgallRikurneawekearner&quot;&gt;gitee&lt;/a&gt; 上，可以使用如下命令行拉取代码。我整个代码仓库比较庞大，使用以下命令行可以进行部分拉取，拉取速度比较快&lt;/p&gt;

&lt;p&gt;先创建一个空文件夹，接着使用命令行 cd 命令进入此空文件夹，在命令行里面输入以下代码，即可获取到本文的代码&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;git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin b678a0cc6d689aa63fd1239bb88113ff8a4b9fcd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上使用的是国内的 gitee 的源，如果 gitee 不能访问，请替换为 github 的源。请在命令行继续输入以下代码，将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码，可以发邮件向我要代码&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;git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin b678a0cc6d689aa63fd1239bb88113ff8a4b9fcd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;获取代码之后，进入 DirectX/D2D/LurqificelgallRikurneawekearner 文件夹，即可获取到源代码&lt;/p&gt;

&lt;h2 id=&quot;特别感谢&quot;&gt;特别感谢&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;特别感谢 &lt;a href=&quot;https://github.com/luojunyuan&quot;&gt;@luojunyuan&lt;/a&gt; 帮忙修复博客示例代码&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;更多博客&quot;&gt;更多博客&lt;/h2&gt;

&lt;p&gt;渲染部分，关于 SharpDx 和 Vortice 的使用方法，包括入门级教程，请参阅：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/WPF-%E4%BD%BF%E7%94%A8-SharpDx-%E6%B8%B2%E6%9F%93%E5%8D%9A%E5%AE%A2%E5%AF%BC%E8%88%AA.html&quot;&gt;渲染博客导航&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/sharpdx.html&quot;&gt;SharpDX 系列&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;更多关于我博客请参阅 &lt;a href=&quot;/post/%E5%8D%9A%E5%AE%A2%E5%AF%BC%E8%88%AA.html&quot;&gt;博客导航&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 07 Apr 2026 22:01:41 +0000</pubDate>
        <link>/post/Vortice-%E4%BD%BF%E7%94%A8-DirectComposition-%E6%98%BE%E7%A4%BA%E9%80%8F%E6%98%8E%E7%AA%97%E5%8F%A3.html</link>
        <guid isPermaLink="true">/post/Vortice-%E4%BD%BF%E7%94%A8-DirectComposition-%E6%98%BE%E7%A4%BA%E9%80%8F%E6%98%8E%E7%AA%97%E5%8F%A3.html</guid>
        
        
        <category>C#</category>
        
        <category>D2D</category>
        
        <category>DirectX</category>
        
        <category>Vortice</category>
        
        <category>Direct2D</category>
        
        <category>渲染</category>
        
        <category>DirectComposition</category>
        
      </item>
    
      <item>
        <title>dotnet 读 WPF 源代码笔记 从 WM_POINTER 消息到 Touch 事件</title>
        <description>&lt;p&gt;本文记录我读 WPF 源代码的笔记，本文将介绍在 WPF 底层是如何从 Win32 的消息循环里获取到的 WM_POINTER 消息处理转换作为 Touch 事件的参数&lt;/p&gt;

&lt;!--more--&gt;

&lt;!-- CreateTime:2024/09/01 07:15:29 --&gt;
&lt;!-- 置顶1 --&gt;
&lt;!-- 发布 --&gt;
&lt;!-- 博客 --&gt;

&lt;p&gt;由于 WPF 触摸部分会兼顾开启 Pointer 消息和不开启 Pointer 消息，在 WPF 框架里面的逻辑会有部分是兼容逻辑，为了方便大家理解，本文分为两个部分。第一个部分是脱离 WPF 框架，聊聊一个 Win32 程序如何从 Win32 的消息循环获取到的 WM_POINTER 消息处理转换为输入坐标点，以及在触摸下获取触摸信息。第二部分是 WPF 框架是如何安排上这些处理逻辑，如何和 WPF 框架的进行对接&lt;/p&gt;

&lt;p&gt;第一部分脱离了 WPF 框架，也就没有了兼容不开启 Pointer 消息的负担，我将使用简单的描述点出关键部分&lt;/p&gt;

&lt;h2 id=&quot;处理-pointer-消息&quot;&gt;处理 Pointer 消息&lt;/h2&gt;

&lt;p&gt;在 Win32 应用程序中，大概有三个方式来进行对 Pointer 消息进行处理。我将从简单到复杂和大家讲述这三个方式&lt;/p&gt;

&lt;h3 id=&quot;最简单获取触摸信息方式&quot;&gt;最简单获取触摸信息方式&lt;/h3&gt;

&lt;p&gt;方式1:&lt;/p&gt;

&lt;p&gt;接收到 WM_POINTER 消息之后，将 wparam 转换为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pointerId&lt;/code&gt; 参数，调用 GetPointerTouchInfo 方法即可获取到 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-pointer_info&quot;&gt;POINTER_INFO&lt;/a&gt; 信息&lt;/p&gt;

&lt;p&gt;获取 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-pointer_info&quot;&gt;POINTER_INFO&lt;/a&gt; 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptPixelLocationRaw&lt;/code&gt; 字段，即可拿到基于屏幕坐标系的像素点&lt;/p&gt;

&lt;p&gt;只需将其转换为窗口坐标系和处理 DPI 即可使用&lt;/p&gt;

&lt;p&gt;此方法的最大缺点在于 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptPixelLocationRaw&lt;/code&gt; 字段拿到的是丢失精度的点，像素为单位。如果在精度稍微高的触摸屏下，将会有明显的锯齿效果&lt;/p&gt;

&lt;p&gt;优点在于其获取特别简单&lt;/p&gt;

&lt;h3 id=&quot;获取高精度触摸点坐标方式&quot;&gt;获取高精度触摸点坐标方式&lt;/h3&gt;

&lt;p&gt;方式2：&lt;/p&gt;

&lt;p&gt;依然是接收到 WM_POINTER 消息之后，将 wparam 转换为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pointerId&lt;/code&gt; 参数，调用 GetPointerTouchInfo 方法即可获取到 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-pointer_info&quot;&gt;POINTER_INFO&lt;/a&gt; 信息&lt;/p&gt;

&lt;p&gt;只是从获取 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-pointer_info&quot;&gt;POINTER_INFO&lt;/a&gt; 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptPixelLocationRaw&lt;/code&gt; 字段换成 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptHimetricLocationRaw&lt;/code&gt; 字段&lt;/p&gt;

&lt;p&gt;使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptHimetricLocationRaw&lt;/code&gt; 字段的优势在于可以获取不丢失精度的信息，但需要额外调用 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerdevicerects&quot;&gt;GetPointerDeviceRects&lt;/a&gt; 函数获取 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;displayRect&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pointerDeviceRect&lt;/code&gt; 信息用于转换坐标点，转换逻辑如以下代码所示&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;PInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPointerDeviceRects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sourceDevice&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;n&quot;&gt;pointerDeviceRect&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;n&quot;&gt;displayRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 如果想要获取比较高精度的触摸点，可以使用 ptHimetricLocationRaw 字段&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 由于 ptHimetricLocationRaw 采用的是 pointerDeviceRect 坐标系，需要转换到屏幕坐标系&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 转换方法就是先将 ptHimetricLocationRaw 的 X 坐标，压缩到 [0-1] 范围内，然后乘以 displayRect 的宽度，再加上 displayRect 的 left 值，即得到了屏幕坐标系的 X 坐标。压缩到 [0-1] 范围内的方法就是除以 pointerDeviceRect 的宽度&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 为什么需要加上 displayRect.left 的值？考虑多屏的情况，屏幕可能是副屏&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Y 坐标同理&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;point2D&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;pointerInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptHimetricLocationRaw&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;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerDeviceRect&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;displayRect&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;displayRect&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;pointerInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptHimetricLocationRaw&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;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerDeviceRect&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;displayRect&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;displayRect&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;// 获取到的屏幕坐标系的点，需要转换到 WPF 坐标系&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 转换过程的两个重点：&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 1. 底层 ClientToScreen 只支持整数类型，直接转换会丢失精度。即使是 WPF 封装的 PointFromScreen 或 PointToScreen 方法也会丢失精度&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 2. 需要进行 DPI 换算，必须要求 DPI 感知&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 先测量窗口与屏幕的偏移量，这里直接取 0 0 点即可，因为这里获取到的是虚拟屏幕坐标系，不需要考虑多屏的情况&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;screenTranslate&lt;/span&gt; &lt;span class=&quot;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;PInvoke&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;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&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;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;screenTranslate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 获取当前的 DPI 值&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dpi&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;GetDpi&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;// 先做平移，再做 DPI 换算&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;point2D&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;point2D&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;screenTranslate&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;point2D&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;screenTranslate&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;point2D&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;point2D&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;dpi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DpiScaleX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;point2D&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;dpi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DpiScaleY&lt;/span&gt;&lt;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;a href=&quot;https://github.com/lindexi/lindexi_gd/tree/322313ee55d0eeaae7148b24ca279e1df087871e/WPFDemo/DefilireceHowemdalaqu&quot;&gt;github&lt;/a&gt; 和 &lt;a href=&quot;https://gitee.com/lindexi/lindexi_gd/tree/322313ee55d0eeaae7148b24ca279e1df087871e/WPFDemo/DefilireceHowemdalaqu&quot;&gt;gitee&lt;/a&gt; 上，可以使用如下命令行拉取代码。我整个代码仓库比较庞大，使用以下命令行可以进行部分拉取，拉取速度比较快&lt;/p&gt;

&lt;p&gt;先创建一个空文件夹，接着使用命令行 cd 命令进入此空文件夹，在命令行里面输入以下代码，即可获取到本文的代码&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;git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 322313ee55d0eeaae7148b24ca279e1df087871e
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上使用的是国内的 gitee 的源，如果 gitee 不能访问，请替换为 github 的源。请在命令行继续输入以下代码，将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码，可以发邮件向我要代码&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;git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 322313ee55d0eeaae7148b24ca279e1df087871e
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;获取代码之后，进入 WPFDemo/DefilireceHowemdalaqu 文件夹，即可获取到源代码&lt;/p&gt;

&lt;p&gt;方式2的优点在于可以获取到更高的精度。缺点是相对来说比较复杂，需要多了点点处理&lt;/p&gt;

&lt;h3 id=&quot;最复杂全功能方式&quot;&gt;最复杂全功能方式&lt;/h3&gt;

&lt;p&gt;方式3：&lt;/p&gt;

&lt;p&gt;此方式会更加复杂，但功能能够更加全面，适合用在要求更高控制的应用里面&lt;/p&gt;

&lt;p&gt;先调用 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerdeviceproperties&quot;&gt;GetPointerDeviceProperties&lt;/a&gt; 方法，获取 HID 描述符上报的对应设备属性，此时可以获取到的是具备完全的 HID 描述符属性的方法，可以包括 &lt;a href=&quot;/post/Windows-%E7%9A%84-Pen-%E5%8D%8F%E8%AE%AE.html&quot;&gt;Windows 的 Pen 协议&lt;/a&gt; 里面列举的各个属性，如宽度高度旋转角等信息&lt;/p&gt;

&lt;p&gt;收到 WM_POINTER 消息时，调用 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getrawpointerdevicedata&quot;&gt;GetRawPointerDeviceData&lt;/a&gt; 获取最原始的触摸信息，再对原始触摸信息进行解析处理&lt;/p&gt;

&lt;p&gt;原始触摸信息的解析处理需要先应用获取每个触摸点的数据包长度，再拆数据包。原始触摸信息拿到的是一个二进制数组，这个二进制数组里面可能包含多个触摸点的信息，需要根据数据包长度拆分为多个触摸点信息&lt;/p&gt;

&lt;p&gt;解析处理就是除了前面两个分别是属于 X 和 Y 之外，后面的数据就根据 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerdeviceproperties&quot;&gt;GetPointerDeviceProperties&lt;/a&gt; 方法获取到的触摸描述信息进行套入&lt;/p&gt;

&lt;p&gt;此方式的复杂程度比较高，且拿到的是原始的触摸信息，需要做比较多的处理。即使解析到 X 和 Y 坐标点之后，还需要执行坐标的转换，将其转换为屏幕坐标系&lt;/p&gt;

&lt;p&gt;这里拿到的 X 和 Y 坐标点是设备坐标系，这里的设备坐标系不是 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerdevicerects&quot;&gt;GetPointerDeviceRects&lt;/a&gt; 函数获取 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pointerDeviceRect&lt;/code&gt; 设备范围坐标系，而是对应 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerdeviceproperties&quot;&gt;GetPointerDeviceProperties&lt;/a&gt; 方法获取到的描述符的逻辑最大值和最小值的坐标范围&lt;/p&gt;

&lt;p&gt;其正确计算方法为从 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerdeviceproperties&quot;&gt;GetPointerDeviceProperties&lt;/a&gt; 方法获取到的 X 和 Y 描述信息，分别取 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-pointer_device_property&quot;&gt;POINTER_DEVICE_PROPERTY&lt;/a&gt; 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;logicalMax&lt;/code&gt; 作为最大值范围。分别将 X 和 Y 除以 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;logicalMax&lt;/code&gt; 缩放到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[0,1]&lt;/code&gt; 范围内，再乘以屏幕尺寸即可转换为屏幕坐标系&lt;/p&gt;

&lt;p&gt;这里的 屏幕尺寸 是通过 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerdevicerects&quot;&gt;GetPointerDeviceRects&lt;/a&gt; 函数获取 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;displayRect&lt;/code&gt; 尺寸&lt;/p&gt;

&lt;p&gt;转换为屏幕坐标系之后，就需要再次处理 DPI 和转换为窗口坐标系的才能使用&lt;/p&gt;

&lt;p&gt;方式3的内容比较多，我将详细过程放在 &lt;a href=&quot;/post/WPF-%E9%80%9A%E8%BF%87-GetRawPointerDeviceData-%E4%BB%8E-WM_POINTER-%E6%B6%88%E6%81%AF%E8%A7%A6%E6%91%B8%E8%A3%B8%E6%95%B0%E6%8D%AE.html&quot;&gt;WPF 通过 GetRawPointerDeviceData 从 WM_POINTER 消息触摸裸数据&lt;/a&gt;
&lt;!-- [WPF 通过 GetRawPointerDeviceData 从 WM_POINTER 消息触摸裸数据 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18890017 ) --&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;nf&quot;&gt;SupportedOSPlatform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;windows5.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;unsafe&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LRESULT&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;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;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;WPARAM&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;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;msg&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WM_POINTERUPDATE&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*Pointer 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;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&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;_oldWndProc&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;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;OperatingSystem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsWindowsVersionAtLeast&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;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;能够收到 WM_Pointer 消息，必定系统版本号不会低&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;pointerId&lt;/span&gt; &lt;span class=&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;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;wParam&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;nf&quot;&gt;GetPointerTouchInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerId&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;POINTER_TOUCH_INFO&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;POINTER_INFO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerInfo&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;pointerInfo&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;Windows&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;Foundation&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;pointerDeviceRect&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;global&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;Win32&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;n&quot;&gt;RECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;displayRect&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;GetPointerDeviceRects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sourceDevice&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;n&quot;&gt;pointerDeviceRect&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;n&quot;&gt;displayRect&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;propertyCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;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;GetPointerDeviceProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sourceDevice&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;n&quot;&gt;propertyCount&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;POINTER_DEVICE_PROPERTY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerDevicePropertyArray&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;n&quot;&gt;POINTER_DEVICE_PROPERTY&lt;/span&gt;&lt;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;propertyCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;GetPointerDeviceProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sourceDevice&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;n&quot;&gt;propertyCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerDevicePropertyArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerDevicePropertySpan&lt;/span&gt; &lt;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;Span&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;POINTER_DEVICE_PROPERTY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerDevicePropertyArray&lt;/span&gt;&lt;span class=&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;propertyCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;nf&quot;&gt;GetPointerCursorId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerId&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;cursorId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;touchInfo&lt;/span&gt; &lt;span class=&quot;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;touchInfo&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;[&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;s&quot;&gt;] &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;touchInfo&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;PointerId=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; CursorId=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cursorId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; PointerDeviceRect=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RectToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerDeviceRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; DisplayRect=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RectToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;displayRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; PropertyCount=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;propertyCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; SourceDevice=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sourceDevice&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;xPropertyIndex&lt;/span&gt; &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;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yPropertyIndex&lt;/span&gt; &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;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;contactIdentifierPropertyIndex&lt;/span&gt; &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;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;widthPropertyIndex&lt;/span&gt; &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;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;heightPropertyIndex&lt;/span&gt; &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;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;pointerDevicePropertySpan&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;POINTER_DEVICE_PROPERTY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerDeviceProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerDevicePropertySpan&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;usagePageId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerDeviceProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usagePageId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;usageId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerDeviceProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usageId&lt;/span&gt;&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;unit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerDeviceProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// 单位指数。 它与 Unit 字段一起定义了设备报告中数据的物理单位。具体来说：&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// - Unit：定义了数据的基本单位，例如厘米、英寸、弧度等。&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// - UnitExponent：表示单位的数量级（即 10 的幂次）。它用于缩放单位值，使其适应不同的范围&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unitExponent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerDeviceProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unitExponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;touchInfo&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;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UsagePageAndIdConverter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&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;usagePageId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;usageId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; Unit=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StylusPointPropertyUnitHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromPointerUnit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unit&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;unit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;) UnitExponent=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unitExponent&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;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;$&quot;  LogicalMin=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerDeviceProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logicalMin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; LogicalMax=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerDeviceProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logicalMax&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;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;$&quot;  PhysicalMin=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerDeviceProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;physicalMin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; PhysicalMax=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerDeviceProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;physicalMax&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;AppendLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usagePageId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;ushort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HidUsagePage&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;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usageId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;ushort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HidUsage&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;n&quot;&gt;xPropertyIndex&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usageId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;ushort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HidUsage&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;n&quot;&gt;yPropertyIndex&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;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;usagePageId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;ushort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HidUsagePage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Digitizer&lt;/span&gt;&lt;span class=&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;usageId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;ushort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DigitizersUsageId&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;widthPropertyIndex&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usageId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;ushort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DigitizersUsageId&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;heightPropertyIndex&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usageId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;ushort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DigitizersUsageId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContactIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;contactIdentifierPropertyIndex&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;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;historyCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;historyCount&lt;/span&gt;&lt;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;rawPointerData&lt;/span&gt; &lt;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;propertyCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;historyCount&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;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawPointerData&lt;/span&gt;&lt;span class=&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;nf&quot;&gt;GetRawPointerDeviceData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;historyCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;pointerDevicePropertyArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pValue&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;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;rawPointerPoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RawPointerPoint&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;historyCount&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;baseIndex&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;propertyCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xPropertyIndex&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;yPropertyIndex&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;xValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawPointerData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;baseIndex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xPropertyIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawPointerData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;baseIndex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yPropertyIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerDevicePropertySpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xPropertyIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerDevicePropertySpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yPropertyIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

                    &lt;span class=&quot;c1&quot;&gt;// 从 Pointer 算到的只能是屏幕坐标的点，转换进应用程序窗口坐标还需要自己再次计算&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xForScreen&lt;/span&gt; &lt;span class=&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;xValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logicalMin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logicalMax&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logicalMin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;displayRect&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;yForScreen&lt;/span&gt; &lt;span class=&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;yValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logicalMin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logicalMax&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logicalMin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;displayRect&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;rawPointerPoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawPointerPoint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;with&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;xForScreen&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;yForScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&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;contactIdentifierPropertyIndex&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;c1&quot;&gt;// 这里的 Id 关联会出现 id 重复的问题，似乎是在上层处理的&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;contactIdentifierValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawPointerData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;baseIndex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;contactIdentifierPropertyIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

                    &lt;span class=&quot;n&quot;&gt;rawPointerPoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawPointerPoint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;with&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;contactIdentifierValue&lt;/span&gt;
                    &lt;span class=&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;widthPropertyIndex&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;heightPropertyIndex&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;widthValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawPointerData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;baseIndex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;widthPropertyIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;heightValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawPointerData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;baseIndex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;heightPropertyIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

                    &lt;span class=&quot;kt&quot;&gt;var&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;n&quot;&gt;pointerDevicePropertySpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;widthPropertyIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&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;n&quot;&gt;pointerDevicePropertySpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;heightPropertyIndex&lt;/span&gt;&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;// 1. 计算出宽度 Value 和最大值最小值的比例&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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;widthScale&lt;/span&gt; &lt;span class=&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;widthValue&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;n&quot;&gt;logicalMin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&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;n&quot;&gt;logicalMax&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;n&quot;&gt;logicalMin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;heightScale&lt;/span&gt; &lt;span class=&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;heightValue&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;n&quot;&gt;logicalMin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&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;n&quot;&gt;logicalMax&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;n&quot;&gt;logicalMin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;widthPixel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;widthScale&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;displayRect&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;heightPixel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;heightScale&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;displayRect&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;rawPointerPoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawPointerPoint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;with&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;RawWidth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;widthValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;RawHeight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;heightValue&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;widthPixel&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;heightPixel&lt;/span&gt;&lt;span class=&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;StylusPointPropertyUnitHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromPointerUnit&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;n&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;StylusPointPropertyUnit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Centimeters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unitExponent&lt;/span&gt; &lt;span class=&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;widthProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unitExponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

                        &lt;span class=&quot;c1&quot;&gt;// 根据 HID 规范，单位指数的值范围是 0x00-0x0F，带上 mask 可以强行约束范围&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HidExponentMask&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x0F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;// HID hut1_6.pdf 23.18.4 Generic Unit Exponent&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;// 以下代码也能从 WPF 的 System.Windows.Input.StylusPointer.PointerStylusPointPropertyInfoHelper 找到&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;unitExponent&lt;/span&gt; &lt;span class=&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;unitExponent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HidExponentMask&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;m&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;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;6&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;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;7&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;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;8&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;m&quot;&gt;8&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;=&amp;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;m&quot;&gt;0x0A&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;m&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                            &lt;span class=&quot;m&quot;&gt;0x0B&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;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                            &lt;span class=&quot;m&quot;&gt;0x0C&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;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                            &lt;span class=&quot;m&quot;&gt;0x0D&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;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                            &lt;span class=&quot;m&quot;&gt;0x0E&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;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                            &lt;span class=&quot;m&quot;&gt;0x0F&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;m&quot;&gt;1&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;n&quot;&gt;unitExponent&lt;/span&gt;
                        &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;// - unchecked((short) (0xFFF0 | 0xA)) == -6&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;// - unchecked((short) (0xFFF0 | 0x9)) == -7&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;//if (unitExponent &amp;gt; 7)&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;//{&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;//    unitExponent = unchecked((short)(0xFFF0 | unitExponent));&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;exponent&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;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unitExponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;widthPhysical&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;widthScale&lt;/span&gt; &lt;span class=&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;n&quot;&gt;physicalMax&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;n&quot;&gt;physicalMin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt;
                                            &lt;span class=&quot;n&quot;&gt;exponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;heightPhysical&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;heightScale&lt;/span&gt; &lt;span class=&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;n&quot;&gt;physicalMax&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;n&quot;&gt;physicalMin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt;
                                             &lt;span class=&quot;n&quot;&gt;exponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

                        &lt;span class=&quot;n&quot;&gt;rawPointerPoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawPointerPoint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;with&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;c1&quot;&gt;// 物理尺寸的计算能够保持和 WPF 的 StylusPoint 拿到的相同&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;PhysicalWidth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;widthPhysical&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;PhysicalHeight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;heightPhysical&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
                    &lt;span class=&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;rawPointerPoint&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;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;span class=&quot;n&quot;&gt;touchInfo&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;PointerPoint PointerId=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; XY=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptPixelLocationRaw&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;pointerInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptPixelLocationRaw&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; rc ContactXY=&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;rcContactRaw&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;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rcContactRaw&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; ContactWH=&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;rcContactRaw&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;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rcContactRaw&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;touchInfo&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;RawPointerPoint Id=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rawPointerPoint&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;s&quot;&gt; XY=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rawPointerPoint&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.00&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;rawPointerPoint&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;0.00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; RawWH=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rawPointerPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RawWidth&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;rawPointerPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RawHeight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; PixelWH=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rawPointerPoint&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;m&quot;&gt;0.00&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;rawPointerPoint&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;m&quot;&gt;0.00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; PhysicalWH=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rawPointerPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PhysicalWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.00&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;rawPointerPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PhysicalHeight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;cm&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// 转换为 WPF 坐标系&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scale&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;GetDpi&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;PixelsPerDip&lt;/span&gt;&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;// 为什么不是在 PointToScreen 传入坐标点，而是传入 0 点呢？这是因为经过了 PointToScreen 方法会丢失精度，即小数点之后的内容会被丢失。因此正常的计算方法都是取 0 点计算出窗口坐标系相对于屏幕坐标系的偏移量&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 减去偏移量之后，再经过 DPI 缩放即可获取窗口坐标系的坐标&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originPointToScreen&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;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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xWpf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rawPointerPoint&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;displayRect&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;originPointToScreen&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;n&quot;&gt;scale&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yWpf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rawPointerPoint&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;displayRect&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;originPointToScreen&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;n&quot;&gt;scale&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;widthWpf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawPointerPoint&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;scale&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;heightWpf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawPointerPoint&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;scale&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;touchInfo&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;RawPointerPoint For WPF XY=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xWpf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.00&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;yWpf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; WH=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;widthWpf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.00&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;heightWpf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.00&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;if&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;IsRealNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xWpf&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;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsRealNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yWpf&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;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsRealNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;widthWpf&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;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsRealNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;heightWpf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;TouchSizeBorder&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;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TouchSizeBorder&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;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TranslateTransform&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;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;xWpf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;widthWpf&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;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;yWpf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;heightWpf&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;TouchSizeBorder&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;widthWpf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;TouchSizeBorder&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;heightWpf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;TouchInfoTextBlock&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;\r\n&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;touchInfo&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;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;_oldWndProc&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;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RectToString&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;Windows&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;Foundation&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;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;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;[XY:&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;s&quot;&gt;;WH:&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;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;rect&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;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;ToInt32&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;n&quot;&gt;wParam&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;ToInt32&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;wParam&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;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;ToInt32&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;=&amp;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;4&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;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;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;ToInt64&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;0xffffffff&lt;/span&gt;&lt;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相对来说还是比较复杂的，但其优点是可以获取到更多的设备描述信息，获取到输入点的更多信息，如可以计算出触摸宽度对应的物理触摸尺寸面积等信息。我写了一个 Demo 采用了方式3获取触摸信息，可以拿到十分具体的触摸信息，包括触摸物体物理尺寸等信息。这里需要强调一点，如果期望在 WPF 获取到触摸物体物理面积等信息，则需要硬件触摸框的功能支持。只有在支持上报正确触摸面积/尺寸的触摸框下，咱才能在 WPF 里面获取到触摸信息&lt;/p&gt;

&lt;p&gt;采用以上方式3的 Demo 的代码放在 &lt;a href=&quot;https://github.com/lindexi/lindexi_gd/tree/c779f7cdf7970ede4fa31f73ff9f92228c7c1498/WPFDemo/NawrernalgarGibehayle&quot;&gt;github&lt;/a&gt; 和 &lt;a href=&quot;https://gitee.com/lindexi/lindexi_gd/blob/c779f7cdf7970ede4fa31f73ff9f92228c7c1498/WPFDemo/NawrernalgarGibehayle&quot;&gt;gitee&lt;/a&gt; 上，可以使用如下命令行拉取代码。我整个代码仓库比较庞大，使用以下命令行可以进行部分拉取，拉取速度比较快&lt;/p&gt;

&lt;p&gt;先创建一个空文件夹，接着使用命令行 cd 命令进入此空文件夹，在命令行里面输入以下代码，即可获取到本文的代码&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;git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin c779f7cdf7970ede4fa31f73ff9f92228c7c1498
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上使用的是国内的 gitee 的源，如果 gitee 不能访问，请替换为 github 的源。请在命令行继续输入以下代码，将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码，可以发邮件向我要代码&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;git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin c779f7cdf7970ede4fa31f73ff9f92228c7c1498
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;获取代码之后，进入 WPFDemo/NawrernalgarGibehayle 文件夹，即可获取到源代码&lt;/p&gt;

&lt;p&gt;对于 WPF 框架来说，自然是选最复杂且功能全强的方法了&lt;/p&gt;

&lt;h2 id=&quot;在-wpf-框架的对接&quot;&gt;在 WPF 框架的对接&lt;/h2&gt;

&lt;p&gt;了解了一个 Win32 应用与 WM_POINTER 消息的对接方式，咱来看看 WPF 具体是如何做的。了解了对接方式之后，阅读 WPF 源代码的方式可以是通过必须调用的方法的引用，找到整个 WPF 的脉络&lt;/p&gt;

&lt;p&gt;在开始之前必须说明的是，本文的大部分代码都是有删减的代码，只保留和本文相关的部分。现在 WPF 是完全开源的，基于最友好的 MIT 协议，可以自己拉下来代码进行二次修改发布，想看完全的代码和调试整个过程可以自己从开源地址拉取整个仓库下来，开源地址是： &lt;a href=&quot;https://github.com/dotnet/wpf&quot;&gt;https://github.com/dotnet/wpf&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;本文以下部分仅为开启 WM_POINTER 消息时的 WPF 框架对接的逻辑，如对不开启 WM_POINTER 支持时的逻辑感兴趣，还请参阅 &lt;a href=&quot;/post/WPF-%E8%A7%A6%E6%91%B8%E5%88%B0%E4%BA%8B%E4%BB%B6.html&quot;&gt;WPF 触摸到事件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;在 WPF 里面，触摸初始化的故事开始是在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PointerTabletDeviceCollection.cs&lt;/code&gt; 里面，调用 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerdevices&quot;&gt;GetPointerDevices&lt;/a&gt; 方法进行初始化获取设备数量，之后的每个设备都调用 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerdeviceproperties&quot;&gt;GetPointerDeviceProperties&lt;/a&gt; 方法，获取 HID 描述符上报的对应设备属性，有删减的代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;System.Windows.Input.StylusPointer&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;/// Maintains a collection of pointer device information for currently installed pointer devices&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;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PointerTabletDeviceCollection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TabletDeviceCollection&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;Refresh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&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;UnsafeNativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;POINTER_DEVICE_INFO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deviceInfos&lt;/span&gt;
                         &lt;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;UnsafeNativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;POINTER_DEVICE_INFO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deviceCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

                    &lt;span class=&quot;n&quot;&gt;IsValid&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;GetPointerDevices&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;deviceCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deviceInfos&lt;/span&gt;&lt;span class=&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;获取到设备之后，将其转换放入到 WPF 定义的 PointerTabletDevice 里面，大概的代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;System.Windows.Input.StylusPointer&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;/// Maintains a collection of pointer device information for currently installed pointer devices&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;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PointerTabletDeviceCollection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TabletDeviceCollection&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;Refresh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&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;UnsafeNativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;POINTER_DEVICE_INFO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deviceInfos&lt;/span&gt;
                         &lt;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;UnsafeNativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;POINTER_DEVICE_INFO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deviceCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

                    &lt;span class=&quot;n&quot;&gt;IsValid&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;GetPointerDevices&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;deviceCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deviceInfos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsValid&lt;/span&gt;&lt;span class=&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;deviceInfo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deviceInfos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;c1&quot;&gt;// Old PenIMC code gets this id via a straight cast from COM pointer address&lt;/span&gt;
                            &lt;span class=&quot;c1&quot;&gt;// into an int32.  This does a very similar thing semantically using the pointer&lt;/span&gt;
                            &lt;span class=&quot;c1&quot;&gt;// to the tablet from the WM_POINTER stack.  While it may have similar issues&lt;/span&gt;
                            &lt;span class=&quot;c1&quot;&gt;// (chopping the upper bits, duplicate ids) we don&apos;t use this id internally&lt;/span&gt;
                            &lt;span class=&quot;c1&quot;&gt;// and have never received complaints about this in the WISP stack.&lt;/span&gt;
                            &lt;span class=&quot;kt&quot;&gt;int&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;MS&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;NativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IntPtrToInt32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deviceInfo&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;PointerTabletDeviceInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptdi&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PointerTabletDeviceInfo&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;deviceInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                            &lt;span class=&quot;c1&quot;&gt;// Don&apos;t add a device that fails initialization.  This means we will try a refresh&lt;/span&gt;
                            &lt;span class=&quot;c1&quot;&gt;// next time around if we receive stylus input and the device is not available.&lt;/span&gt;
                            &lt;span class=&quot;c1&quot;&gt;// &amp;lt;see cref=&quot;HwndPointerInputProvider.UpdateCurrentTabletAndStylus&quot;&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;ptdi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryInitialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
                            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;PointerTabletDevice&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tablet&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PointerTabletDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptdi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                                &lt;span class=&quot;n&quot;&gt;_tabletDeviceMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tablet&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;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tablet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;TabletDevices&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;tablet&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;p&quot;&gt;}&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&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;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// Holds a mapping of TabletDevices from their WM_POINTER device id&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;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&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;PointerTabletDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_tabletDeviceMap&lt;/span&gt; &lt;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;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PointerTabletDevice&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;k&quot;&gt;namespace&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;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///     Collection of the tablet devices that are available on the machine.&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;TabletDeviceCollection&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;,&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;internal&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;TabletDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TabletDevices&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;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;TabletDevice&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在 PointerTabletDeviceInfo 的 TryInitialize 方法，即 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if (ptdi.TryInitialize())&lt;/code&gt; 这行代码里面，将会调用 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerdeviceproperties&quot;&gt;GetPointerDeviceProperties&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;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Input.StylusPointer&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;/// WM_POINTER specific information about a TabletDevice&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;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PointerTabletDeviceInfo&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;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PointerTabletDeviceInfo&lt;/span&gt;&lt;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;id&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;POINTER_DEVICE_INFO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deviceInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_deviceInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deviceInfo&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;id&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;_deviceInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;productString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;PlugAndPlayId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_deviceInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;productString&lt;/span&gt;&lt;span class=&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;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TryInitialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&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;success&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TryInitializeSupportedStylusPointProperties&lt;/span&gt;&lt;span class=&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;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;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TryInitializeSupportedStylusPointProperties&lt;/span&gt;&lt;span class=&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;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;// 忽略其他代码&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// Retrieve all properties from the WM_POINTER stack&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;n&quot;&gt;UnsafeNativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPointerDeviceProperties&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;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propCount&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;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;n&quot;&gt;UnsafeNativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPointerDeviceProperties&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;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SupportedPointerProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                &lt;span class=&quot;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;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;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;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// The specific id for this TabletDevice&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;n&quot;&gt;IntPtr&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;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;_deviceInfo&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;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;/// Store the WM_POINTER device information directly&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;UnsafeNativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;POINTER_DEVICE_INFO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_deviceInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span 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://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerdeviceproperties&quot;&gt;GetPointerDeviceProperties&lt;/a&gt; 两次？第一次只是拿数量，第二次才是真正的拿值&lt;/p&gt;

&lt;p&gt;回顾以上代码，可以看到 PointerTabletDeviceInfo 对象是在 PointerTabletDeviceCollection 的 Refresh 方法里面创建的，如以下代码所示&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;PointerTabletDeviceCollection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TabletDeviceCollection&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;Refresh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&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;UnsafeNativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;POINTER_DEVICE_INFO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deviceInfos&lt;/span&gt;
                         &lt;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;UnsafeNativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;POINTER_DEVICE_INFO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deviceCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

                    &lt;span class=&quot;n&quot;&gt;IsValid&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;GetPointerDevices&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;deviceCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deviceInfos&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;deviceInfo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deviceInfos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;c1&quot;&gt;// Old PenIMC code gets this id via a straight cast from COM pointer address&lt;/span&gt;
                            &lt;span class=&quot;c1&quot;&gt;// into an int32.  This does a very similar thing semantically using the pointer&lt;/span&gt;
                            &lt;span class=&quot;c1&quot;&gt;// to the tablet from the WM_POINTER stack.  While it may have similar issues&lt;/span&gt;
                            &lt;span class=&quot;c1&quot;&gt;// (chopping the upper bits, duplicate ids) we don&apos;t use this id internally&lt;/span&gt;
                            &lt;span class=&quot;c1&quot;&gt;// and have never received complaints about this in the WISP stack.&lt;/span&gt;
                            &lt;span class=&quot;kt&quot;&gt;int&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;MS&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;NativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IntPtrToInt32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deviceInfo&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;PointerTabletDeviceInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptdi&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PointerTabletDeviceInfo&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;deviceInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptdi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryInitialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
                            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                                
                            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                        &lt;span class=&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;从 GetPointerDevices 获取到的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POINTER_DEVICE_INFO&lt;/code&gt; 信息会存放在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PointerTabletDeviceInfo&lt;/code&gt; 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_deviceInfo&lt;/code&gt; 字段里面，如下面代码所示&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;PointerTabletDeviceInfo&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;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PointerTabletDeviceInfo&lt;/span&gt;&lt;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;id&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;POINTER_DEVICE_INFO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deviceInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_deviceInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deviceInfo&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;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;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// The specific id for this TabletDevice&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;n&quot;&gt;IntPtr&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;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;_deviceInfo&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;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;/// Store the WM_POINTER device information directly&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;UnsafeNativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;POINTER_DEVICE_INFO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_deviceInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span 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://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerdeviceproperties&quot;&gt;GetPointerDeviceProperties&lt;/a&gt; 时，就会将 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POINTER_DEVICE_INFO&lt;/code&gt; 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;device&lt;/code&gt; 字段作为参数传入，从而获取到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POINTER_DEVICE_PROPERTY&lt;/code&gt; 结构体列表信息&lt;/p&gt;

&lt;p&gt;获取到的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POINTER_DEVICE_PROPERTY&lt;/code&gt; 结构体信息和 HID 描述符上报的信息非常对应。结构体的定义代码大概如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;/// A struct representing the information for a particular pointer property.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// These correspond to the raw data from WM_POINTER.&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;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;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;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;POINTER_DEVICE_PROPERTY&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;Int32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logicalMin&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;Int32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logicalMax&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;Int32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;physicalMin&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;Int32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;physicalMax&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;UInt32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unit&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;UInt32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unitExponent&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;UInt16&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;usagePageId&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;UInt16&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;usageId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;根据 HID 基础知识可以知道，通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;usagePageId&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;usageId&lt;/code&gt; 即可了解到此设备属性的具体含义。更多请参阅 HID 标准文档： &lt;a href=&quot;http://www.usb.org/developers/hidpage/Hut1_12v2.pdf&quot;&gt;http://www.usb.org/developers/hidpage/Hut1_12v2.pdf&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;在 WPF 使用到的 Pointer 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;usagePageId&lt;/code&gt; 只有以下枚举所列举的值&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;/// WM_POINTER stack must parse out HID spec usage pages&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;see cref=&quot;http://www.usb.org/developers/hidpage/Hut1_12v2.pdf&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;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HidUsagePage&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Undefined&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;n&quot;&gt;Generic&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;n&quot;&gt;Simulation&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;n&quot;&gt;Vr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x03&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Sport&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;n&quot;&gt;Game&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x05&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;m&quot;&gt;0x07&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Led&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;n&quot;&gt;Button&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x09&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;m&quot;&gt;0x0a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Telephony&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x0b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Consumer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x0c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Digitizer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x0d&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;m&quot;&gt;0x10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Alphanumeric&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;BarcodeScanner&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x8C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;WeighingDevice&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x8D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;MagneticStripeReader&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x8E&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;CameraControl&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x90&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;MicrosoftBluetoothHandsfree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0xfff3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;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 使用到的 Pointer 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;usageId&lt;/code&gt; 只有以下枚举所列举的值&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;/// &lt;/span&gt;
       &lt;span class=&quot;c1&quot;&gt;/// WISP pre-parsed these, WM_POINTER stack must do it itself&lt;/span&gt;
       &lt;span class=&quot;c1&quot;&gt;/// &lt;/span&gt;
       &lt;span class=&quot;c1&quot;&gt;/// See Stylus\biblio.txt - 1&lt;/span&gt;
       &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;see cref=&quot;http://www.usb.org/developers/hidpage/Hut1_12v2.pdf&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;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HidUsage&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
           &lt;span class=&quot;n&quot;&gt;TipPressure&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x30&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;0x30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;n&quot;&gt;BarrelPressure&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x31&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;0x31&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&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;m&quot;&gt;0x32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;n&quot;&gt;XTilt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x3D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;n&quot;&gt;YTilt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x3E&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;n&quot;&gt;Azimuth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x3F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;n&quot;&gt;Altitude&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;n&quot;&gt;Twist&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x41&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;n&quot;&gt;TipSwitch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x42&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;n&quot;&gt;SecondaryTipSwitch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x43&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;n&quot;&gt;BarrelSwitch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x44&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;n&quot;&gt;TouchConfidence&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x47&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;0x48&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;0x49&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;n&quot;&gt;TransducerSerialNumber&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x5B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
       &lt;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 的古老版本里面，约定了使用 GUID 去获取 StylusPointDescription 里面的额外数据信息。为了与此行为兼容，在 WPF 里面就定义了 HidUsagePage 和 HidUsage 与 GUID 的对应关系，实现代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;System.Windows.Input&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;/// StylusPointPropertyIds&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;ExternalAPI/&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;StylusPointPropertyIds&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 x-coordinate in the tablet coordinate space.&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;ExternalAPI/&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;Guid&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;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Guid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0x598A6A8F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x52C0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x4BA0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x93&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0xAF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0xAF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x35&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x74&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0xA5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x61&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 y-coordinate in the tablet coordinate space.&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;ExternalAPI/&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;Guid&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;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Guid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0xB53F9F75&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x04E0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x4498&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0xA7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0xEE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0xC3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x0D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0xBB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x5A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x90&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x11&lt;/span&gt;&lt;span class=&quot;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;Guid&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;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;/// WM_POINTER stack usage preparation based on associations maintained from the legacy WISP based stack&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;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HidUsagePage&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;HidUsage&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;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_hidToGuidMap&lt;/span&gt; &lt;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;HidUsagePage&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;HidUsage&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;&amp;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;HidUsagePage&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;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;HidUsage&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;&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;HidUsage&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;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HidUsage&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;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;n&quot;&gt;HidUsage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&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;Z&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HidUsagePage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Digitizer&lt;/span&gt;&lt;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;HidUsage&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;&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;HidUsage&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;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;HidUsage&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;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;HidUsage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TouchConfidence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SystemTouch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HidUsage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TipPressure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NormalPressure&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HidUsage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BarrelPressure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ButtonPressure&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HidUsage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;XTilt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;XTiltOrientation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HidUsage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;YTilt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;YTiltOrientation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HidUsage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Azimuth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AzimuthOrientation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HidUsage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Altitude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AltitudeOrientation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HidUsage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Twist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TwistOrientation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HidUsage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TipSwitch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TipButton&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HidUsage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecondaryTipSwitch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SecondaryTipButton&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HidUsage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BarrelSwitch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BarrelButton&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HidUsage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TransducerSerialNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SerialNumber&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&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;/// Retrieves the GUID of the stylus property associated with the usage page and usage ids&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// within the HID specification.&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;page&quot;&amp;gt;The usage page id of the HID specification&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;usage&quot;&amp;gt;The usage id of the HID specification&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 known, the GUID associated with the usagePageId and usageId.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// If not known, GUID.Empty&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&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;n&quot;&gt;Guid&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetKnownGuid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HidUsagePage&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;n&quot;&gt;HidUsage&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;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Guid&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;Guid&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;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HidUsage&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;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pageMap&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;_hidToGuidMap&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;page&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;pageMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;pageMap&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;usage&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;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;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;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;通过以上的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_hidToGuidMap&lt;/code&gt; 的定义关联关系，调用 GetKnownGuid 方法，即可将 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POINTER_DEVICE_PROPERTY&lt;/code&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;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Input.StylusPointer&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;/// Contains a WM_POINTER specific functions to parse out stylus property info&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;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PointerStylusPointPropertyInfoHelper&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;/// Creates WPF property infos from WM_POINTER device properties.  This appropriately maps and converts HID spec&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// properties found in WM_POINTER to their WPF equivalents.  This is based on code from the WISP implementation&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// that feeds the legacy WISP based stack.&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;prop&quot;&amp;gt;The pointer property to convert&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;The equivalent WPF property info&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;n&quot;&gt;StylusPointPropertyInfo&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreatePropertyInfo&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;POINTER_DEVICE_PROPERTY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;StylusPointPropertyInfo&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;c1&quot;&gt;// Get the mapped GUID for the HID usages&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Guid&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propGuid&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;StylusPointPropertyIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetKnownGuid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StylusPointPropertyIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HidUsagePage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usagePageId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StylusPointPropertyIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HidUsage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usageId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;propGuid&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;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;n&quot;&gt;StylusPointProperty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stylusProp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StylusPointProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;propGuid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StylusPointPropertyIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsKnownButton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;propGuid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

                &lt;span class=&quot;c1&quot;&gt;// Set Units&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;StylusPointPropertyUnit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StylusPointPropertyUnitHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromPointerUnit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                &lt;span class=&quot;c1&quot;&gt;// If the parsed unit is invalid, set the default&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unit&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;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;unit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StylusPointPropertyInfoDefaults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetStylusPointPropertyInfoDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stylusProp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Unit&lt;/span&gt;&lt;span class=&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 to default resolution&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resolution&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StylusPointPropertyInfoDefaults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetStylusPointPropertyInfoDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stylusProp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Resolution&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;n&quot;&gt;mappedExponent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;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;_hidExponentMap&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;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unitExponent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HidExponentMask&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;mappedExponent&lt;/span&gt;&lt;span class=&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;exponent&lt;/span&gt; &lt;span class=&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;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;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mappedExponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                    &lt;span class=&quot;c1&quot;&gt;// Guard against divide by zero or negative resolution&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;physicalMax&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;physicalMin&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;c1&quot;&gt;// Calculated resolution is a scaling factor from logical units into the physical space&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;// at the given exponentiation.&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;resolution&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
                            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logicalMax&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logicalMin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;physicalMax&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;physicalMin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&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;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StylusPointPropertyInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                      &lt;span class=&quot;n&quot;&gt;stylusProp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;n&quot;&gt;prop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logicalMin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;n&quot;&gt;prop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logicalMax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;n&quot;&gt;unit&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;resolution&lt;/span&gt;&lt;span class=&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;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上的一个小细节点在于对 unit 单位的处理，即 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StylusPointPropertyUnit? unit = StylusPointPropertyUnitHelper.FromPointerUnit(prop.unit);&lt;/code&gt; 这行代码的实现定义，具体实现如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StylusPointPropertyUnitHelper&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;/// Convert WM_POINTER units to WPF units&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;pointerUnit&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;n&quot;&gt;StylusPointPropertyUnit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromPointerUnit&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;pointerUnit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;StylusPointPropertyUnit&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StylusPointPropertyUnit&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;_pointerUnitMap&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;pointerUnit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UNIT_MASK&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;unit&lt;/span&gt;&lt;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;IsDefined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StylusPointPropertyUnit&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;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// Mapping for WM_POINTER based unit, taken from legacy WISP code&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;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&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;StylusPointPropertyUnit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_pointerUnitMap&lt;/span&gt; &lt;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;uint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StylusPointPropertyUnit&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;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StylusPointPropertyUnit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Centimeters&lt;/span&gt; &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;n&quot;&gt;StylusPointPropertyUnit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Radians&lt;/span&gt; &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;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StylusPointPropertyUnit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Inches&lt;/span&gt; &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;n&quot;&gt;StylusPointPropertyUnit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Degrees&lt;/span&gt; &lt;span class=&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;/// Mask to extract units from raw WM_POINTER data&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;see cref=&quot;http://www.usb.org/developers/hidpage/Hut1_12v2.pdf&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;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UNIT_MASK&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x000F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里的单位的作用是什么呢？用于和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POINTER_DEVICE_PROPERTY&lt;/code&gt; 的物理值做关联对应关系，比如触摸面积 Width 和 Height 的物理尺寸就是通过大概如下算法计算出来的&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;short&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mappedExponent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;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;_hidExponentMap&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;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unitExponent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HidExponentMask&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;mappedExponent&lt;/span&gt;&lt;span class=&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;exponent&lt;/span&gt; &lt;span class=&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;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;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mappedExponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                    &lt;span class=&quot;c1&quot;&gt;// Guard against divide by zero or negative resolution&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;physicalMax&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;physicalMin&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;c1&quot;&gt;// Calculated resolution is a scaling factor from logical units into the physical space&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;// at the given exponentiation.&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;resolution&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
                            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logicalMax&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logicalMin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;physicalMax&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;physicalMin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&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;/// Contains the mappings from WM_POINTER exponents to our local supported values.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// This mapping is taken from WISP code, see Stylus\Biblio.txt - 4,&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// as an array of HidExponents.&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;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&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;kt&quot;&gt;short&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_hidExponentMap&lt;/span&gt; &lt;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;byte&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;&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;m&quot;&gt;5&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;m&quot;&gt;6&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;m&quot;&gt;7&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;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;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;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;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;m&quot;&gt;0xA&lt;/span&gt;&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;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;m&quot;&gt;0xB&lt;/span&gt;&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;m&quot;&gt;0xC&lt;/span&gt;&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;m&quot;&gt;0xD&lt;/span&gt;&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;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;0xE&lt;/span&gt;&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;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0xF&lt;/span&gt;&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;p&gt;通过 resolution 与具体后续收到的触摸点的值进行计算，带上 StylusPointPropertyUnit 单位，这就是触摸设备上报的物理尺寸了&lt;/p&gt;

&lt;p&gt;以上 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;logicalMax&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;logicalMin&lt;/code&gt; 在行业内常被称为逻辑值，以上的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;physicalMax&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;physicalMin&lt;/code&gt; 常被称为物理值&lt;/p&gt;

&lt;!-- 许多触摸设备厂商，特别是大尺寸的红外框，都默认将逻辑值和物理值设置为相同的值 --&gt;

&lt;p&gt;经过以上的处理之后，即可将 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerdeviceproperties&quot;&gt;GetPointerDeviceProperties&lt;/a&gt; 拿到的设备属性列表给转换为 WPF 框架对应的定义属性内容&lt;/p&gt;

&lt;p&gt;以上过程有一个细节，那就是 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerdeviceproperties&quot;&gt;GetPointerDeviceProperties&lt;/a&gt; 拿到的设备属性列表的顺序是非常关键的，设备属性列表的顺序和在后续 WM_POINTER 消息拿到的裸数据的顺序是直接对应的&lt;/p&gt;

&lt;p&gt;大家可以看到，在开启 Pointer 消息时，触摸模块初始化获取触摸信息是完全通过 Win32 的 WM_POINTER 模块提供的相关方法完成的。这里需要和不开 WM_POINTER 消息的从 COM 获取触摸设备信息区分，和 &lt;a href=&quot;/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-%E6%8F%92%E5%85%A5%E8%A7%A6%E6%91%B8%E8%AE%BE%E5%A4%87%E7%9A%84%E5%88%9D%E5%A7%8B%E5%8C%96%E8%8E%B7%E5%8F%96%E8%AE%BE%E5%A4%87%E4%BF%A1%E6%81%AF.html&quot;&gt;dotnet 读 WPF 源代码笔记 插入触摸设备的初始化获取设备信息&lt;/a&gt; 提供的方法是不相同的&lt;/p&gt;

&lt;p&gt;完成上述初始化逻辑之后，接下来看看消息循环收到 WM_POINTER 消息的处理&lt;/p&gt;

&lt;p&gt;收到 WM_POINTER 消息时，调用 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getrawpointerdevicedata&quot;&gt;GetRawPointerDeviceData&lt;/a&gt; 获取最原始的触摸信息，再对原始触摸信息进行解析处理&lt;/p&gt;

&lt;p&gt;在 WPF 里面，大家都知道，底层的消息循环处理的在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HwndSource.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;namespace&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;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///     The HwndSource class presents content within a Win32 HWND.&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;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;n&quot;&gt;IDisposable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IWin32Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IKeyboardInputSink&lt;/span&gt;
    &lt;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;InputFilterMessage&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;p&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 忽略其他代码&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// NOTE (alexz): invoke _stylus.FilterMessage before _mouse.FilterMessage&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// to give _stylus a chance to eat mouse message generated by stylus&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&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;&amp;amp;&amp;amp;&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;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;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;n&quot;&gt;result&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;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FilterMessage&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;message&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;ref&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;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;n&quot;&gt;SecurityCriticalDataClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IStylusInputProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;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;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上代码的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_stylus&lt;/code&gt; 就是根据不同的配置参数决定是否使用 Pointer 消息处理的 HwndPointerInputProvider 类型，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;System.Windows.Interop&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 HwndSource class presents content within a Win32 HWND.&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;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;n&quot;&gt;IDisposable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IWin32Window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IKeyboardInputSink&lt;/span&gt;
    &lt;span class=&quot;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;Initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HwndSourceParameters&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;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;StylusLogic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsStylusAndTouchSupportEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// Choose between Wisp and Pointer stacks&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StylusLogic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsPointerStackEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&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;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SecurityCriticalDataClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IStylusInputProvider&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;HwndPointerInputProvider&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;else&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;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SecurityCriticalDataClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IStylusInputProvider&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;HwndStylusInputProvider&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;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;在本文这里初始化的是 HwndPointerInputProvider 类型，将会进入到 HwndPointerInputProvider 的 FilterMessage 方法处理输入数据&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;System.Windows.Interop&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;/// Implements an input provider per hwnd for WM_POINTER messages&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;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HwndPointerInputProvider&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;IStylusInputProvider&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;/// Processes the message loop for the HwndSource, filtering WM_POINTER messages where needed&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;The hwnd the message is for&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;msg&quot;&amp;gt;The message&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;wParam&quot;&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;lParam&quot;&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;handled&quot;&amp;gt;If this has been successfully processed&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;n&quot;&gt;IntPtr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IStylusInputProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FilterMessage&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;WindowMessage&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;n&quot;&gt;handled&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;// Do not process any messages if the stack was disabled via reflection hack&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PointerLogic&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;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;WindowMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WM_ENABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;IsWindowEnabled&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;Win32&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;IntPtrToInt32&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;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;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;WindowMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WM_POINTERENTER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;c1&quot;&gt;// Enter can be processed as an InRange.  &lt;/span&gt;
                            &lt;span class=&quot;c1&quot;&gt;// The MSDN documentation is not correct for InRange (according to feisu)&lt;/span&gt;
                            &lt;span class=&quot;c1&quot;&gt;// As such, using enter is the correct way to generate this.  This is also what DirectInk uses.&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;nf&quot;&gt;ProcessMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPointerId&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;RawStylusActions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InRange&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;TickCount&lt;/span&gt;&lt;span class=&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;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WindowMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WM_POINTERUPDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&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;nf&quot;&gt;ProcessMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPointerId&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;RawStylusActions&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;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TickCount&lt;/span&gt;&lt;span class=&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;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WindowMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WM_POINTERDOWN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&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;nf&quot;&gt;ProcessMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPointerId&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;RawStylusActions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Down&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;TickCount&lt;/span&gt;&lt;span class=&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;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WindowMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WM_POINTERUP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&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;nf&quot;&gt;ProcessMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPointerId&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;RawStylusActions&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;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TickCount&lt;/span&gt;&lt;span class=&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;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WindowMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WM_POINTERLEAVE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;c1&quot;&gt;// Leave can be processed as an OutOfRange.  &lt;/span&gt;
                            &lt;span class=&quot;c1&quot;&gt;// The MSDN documentation is not correct for OutOfRange (according to feisu)&lt;/span&gt;
                            &lt;span class=&quot;c1&quot;&gt;// As such, using leave is the correct way to generate this.  This is also what DirectInk uses.&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;nf&quot;&gt;ProcessMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPointerId&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;RawStylusActions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OutOfRange&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;TickCount&lt;/span&gt;&lt;span class=&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;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;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;对于收到 Pointer 的按下移动抬起消息，都会进入到 ProcessMessage 方法&lt;/p&gt;

&lt;p&gt;进入之前调用的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetPointerId(wParam)&lt;/code&gt; 代码的 GetPointerId 方法实现如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;/// Extracts the pointer id&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;wParam&quot;&amp;gt;The parameter containing the id&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;The pointer id&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;uint&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetPointerId&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;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;uint&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;Win32&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;SignedLOWORD&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;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;internal&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;NativeMethods&lt;/span&gt;
    &lt;span class=&quot;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;SignedLOWORD&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;intPtr&lt;/span&gt;&lt;span class=&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;SignedLOWORD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IntPtrToInt32&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;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;IntPtrToInt32&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;intPtr&lt;/span&gt;&lt;span class=&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;unchecked&lt;/span&gt;&lt;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;intPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToInt64&lt;/span&gt;&lt;span class=&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;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SignedLOWORD&lt;/span&gt;&lt;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;n&lt;/span&gt;&lt;span class=&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;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;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;n&quot;&gt;n&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;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;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;pointerId&lt;/span&gt; &lt;span class=&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;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;wparam&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在 WM_POINTER 的设计上，将会源源不断通过消息循环发送指针消息，发送的指针消息里面不直接包含具体的数据信息，而是只将 PointerId 当成 wparam 发送。咱从消息循环里面拿到的只有 PointerId 的值，转换方法如上述代码所示&lt;/p&gt;

&lt;p&gt;为什么是这样设计的呢？考虑到现在大部分触摸屏的精度都不低，至少比许多很便宜鼠标的高，这就可能导致应用程序完全无法顶得住每次触摸数据过来都通过消息循环怼进来。在 WM_POINTER 的设计上，只是将 PointerId 通过消息循环发送过来，具体的消息体数据需要使用 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerinfo&quot;&gt;GetPointerInfo&lt;/a&gt; 方法来获取。这么设计有什么优势？这么设计是用来解决应用卡顿的时候，被堆积消息的问题。假定现在有三个触摸消息进来，第一个触摸消息进来就发送了 Win32 消息给到应用，然而应用等待到系统收集到了三个触摸点消息时，才调用 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerinfo&quot;&gt;GetPointerInfo&lt;/a&gt; 方法。那此时系统触摸模块就可以很开森的知道了应用处于卡顿状态，即第二个和第三个触摸消息到来时，判断第一个消息还没被应用消费，就不再发送 Win32 消息给到应用。当应用调用 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerinfo&quot;&gt;GetPointerInfo&lt;/a&gt; 方法时，就直接返回第三个点给到应用，跳过中间第一个和第二个触摸点。同时，使用历史点的概念，将第一个点和第二个点和第三个点给到应用，如果此时应用感兴趣的话&lt;/p&gt;

&lt;p&gt;利用如上所述机制，即可实现到当触摸设备产生的触摸消息过快时，不会让应用的消息循环过度忙碌，而是可以让应用有机会一次性拿到过去一段时间内的多个触摸点信息。如此可以提升整体系统的性能，减少应用程序忙碌于处理过往的触摸消息&lt;/p&gt;

&lt;p&gt;举一个虚拟的例子，让大家更好的理解这套机制的思想。假定咱在制作一个应用，应用有一个功能，就是有一个矩形元素，这个元素可以响应触摸拖动，可以用触摸拖动矩形元素。这个应用编写的有些离谱，每次拖动的做法就是设置新的坐标点为当前触摸点，但是这个过程需要 15 毫秒，因为中间添加了一些有趣且保密（其实我还没编出来）的算法。当应用跑在一个触摸设备上，这个触摸设备在触摸拖动的过程中，每 10 毫秒将产生一次触摸点信息报告给到系统。假定当前的系统的触摸模块是如实的每次收到设备发送过来的触摸点，都通过 Win32 消息发送给到应用，那将会让应用的消费速度慢于消息的生产速度，这就意味着大家可以明显看到拖动矩形元素时具备很大的延迟感。如拖着拖着才发现矩形元素还在后面慢慢挪动，整体的体验比较糟糕。那如果采用现在的这套玩法呢？应用程序从 Win32 消息收到的是 PointerId 信息，再通过 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerinfo&quot;&gt;GetPointerInfo&lt;/a&gt; 方法获取触摸点信息，此时获取到的触摸点就是最后一个触摸点，对于咱这个应用来说刚刚好，直接就是响应设置矩形元素坐标为最后一个触摸点的对应坐标。如此即可看到矩形元素飞快跳着走，且由于刚好矩形元素拖动过程为 15 毫秒，小于 16 毫秒，意味着大部分情况下大家看到的是矩形元素平滑的移动，即飞快跳着走在人类看来是一个连续移动的过程&lt;/p&gt;

&lt;p&gt;期望通过以上的例子可以让大家了解到微软的“良苦”用心&lt;/p&gt;

&lt;!-- DirectInk --&gt;

&lt;p&gt;这里需要额外说明的是 PointerId 和 TouchDevice 等的 Id 是不一样的，在下文将会给出详细的描述&lt;/p&gt;

&lt;p&gt;在 WPF 这边，如上面代码所示，收到触摸点信息之后，将会进入到 ProcessMessage 方法，只是这个过程中我感觉有一点小锅的是，时间戳拿的是当前系统时间戳 Environment.TickCount 的值，而不是取 Pointer 消息里面的时间戳内容&lt;/p&gt;

&lt;p&gt;继续看一下 ProcessMessage 方法的定义和实现&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;System.Windows.Interop&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;/// Implements an input provider per hwnd for WM_POINTER messages&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;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HwndPointerInputProvider&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;IStylusInputProvider&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;/// Processes the latest WM_POINTER message and forwards it to the WPF input stack.&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;pointerId&quot;&amp;gt;The id of the pointer message&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;action&quot;&amp;gt;The stylus action being done&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;timestamp&quot;&amp;gt;The time (in ticks) the message arrived&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;True if successfully processed (handled), false otherwise&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;ProcessMessage&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;pointerId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RawStylusActions&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;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&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;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;在 ProcessMessage 里面将创建 PointerData 对象，这个 PointerData 类型是一个辅助类，在构造函数里面将调用 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerinfo&quot;&gt;GetPointerInfo&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;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ProcessMessage&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;pointerId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RawStylusActions&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;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&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;handled&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;// Acquire all pointer data needed&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;PointerData&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;PointerData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerId&lt;/span&gt;&lt;span class=&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;以下是 PointerData 构造函数的简单定义的有删减的代码&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;System.Windows.Input.StylusPointer&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;/// Provides a wrapping class that aggregates Pointer data from a pointer event/message&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;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PointerData&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;/// Queries all needed data from a particular pointer message and stores&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// it locally.&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;pointerId&quot;&amp;gt;The id of the pointer message&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PointerData&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;pointerId&lt;/span&gt;&lt;span class=&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;IsValid&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetPointerInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerId&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;_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;_history&lt;/span&gt; &lt;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;POINTER_INFO&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;historyCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

                &lt;span class=&quot;c1&quot;&gt;// Fill the pointer history&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// If we fail just return a blank history&lt;/span&gt;
                &lt;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;GetPointerInfoHistory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerId&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;_info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;historyCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;_history&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;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;POINTER_INFO&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;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;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// Standard pointer information&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;POINTER_INFO&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;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// The full history available for the current pointer (used for coalesced input)&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;POINTER_INFO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_history&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;/// If true, we have correctly queried pointer data, false otherwise.&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;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsValid&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;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;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerinfo&quot;&gt;GetPointerInfo&lt;/a&gt; 方法获取指针点信息。在 WPF 的基础事件里面也是支持历史点的，意图和 Pointer 的设计意图差不多，都是为了解决业务端的消费数据速度问题。于是在 WPF 底层也就立刻调用 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerinfohistory&quot;&gt;GetPointerInfoHistory&lt;/a&gt; 获取历史点信息&lt;/p&gt;

&lt;p&gt;对于 Pointer 消息来说，对触摸和触笔有着不同的数据提供分支，分别是 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointertouchinfo&quot;&gt;GetPointerTouchInfo&lt;/a&gt; 方法和 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerpeninfo&quot;&gt;GetPointerPenInfo&lt;/a&gt; 方法&lt;/p&gt;

&lt;p&gt;在 PointerData 构造函数里面，也通过判断 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POINTER_INFO&lt;/code&gt; 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pointerType&lt;/code&gt; 字段决定调用不同的方法，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;IsValid&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetPointerInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerId&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;_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;switch&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;pointerType&lt;/span&gt;&lt;span class=&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;POINTER_INPUT_TYPE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PT_TOUCH&lt;/span&gt;&lt;span class=&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 we have a touch device, pull the touch specific information down&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;IsValid&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetPointerTouchInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerId&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;_touchInfo&lt;/span&gt;&lt;span class=&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;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;POINTER_INPUT_TYPE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PT_PEN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;c1&quot;&gt;// Otherwise we have a pen device, so pull down pen specific information&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;IsValid&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetPointerPenInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerId&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;_penInfo&lt;/span&gt;&lt;span class=&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;k&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;c1&quot;&gt;// Only process touch or pen messages, do not process mouse or touchpad&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;IsValid&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;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;对于 WPF 的 HwndPointerInputProvider 模块来说，只处理 PT_TOUCH 和 PT_PEN 消息，即触摸和触笔消息。对于 Mouse 鼠标和 Touchpad 触摸板来说都不走 Pointer 处理，依然是走原来的 Win32 消息。为什么这么设计呢？因为 WPF 里面没有 Pointer 路由事件，在 WPF 里面分开了 Touch 和 Stylus 和 Mouse 事件。就不需要全部都在 Pointer 模块处理了，依然在原来的消息循环里面处理，既减少 Pointer 模块的工作量，也能减少后续从 Pointer 分发到 Touch 和 Stylus 和 Mouse 事件的工作量。原先的模块看起来也跑得很稳，那就一起都不改了&lt;/p&gt;

&lt;p&gt;完成 PointerData 的构造函数之后，继续到 HwndPointerInputProvider 的 ProcessMessage 函数里面，在此函数里面判断是 PT_TOUCH 和 PT_PEN 消息，则进行处理&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ProcessMessage&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;pointerId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RawStylusActions&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;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&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;handled&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;// Acquire all pointer data needed&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;PointerData&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;PointerData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// Only process touch or pen messages, do not process mouse or touchpad&lt;/span&gt;
            &lt;span class=&quot;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;IsValid&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;Info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerType&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;POINTER_INPUT_TYPE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PT_TOUCH&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;Info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerType&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;POINTER_INPUT_TYPE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PT_PEN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&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;return&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对于触摸和触笔的处理上，先是执行触摸设备关联。触摸设备关联一个在上层业务的表现就是让当前的指针消息关联上 TouchDevice 的 Id 或 StylusDevice 的 Id 值&lt;/p&gt;

&lt;p&gt;关联的方法是通过 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointercursorid&quot;&gt;GetPointerCursorId&lt;/a&gt; 方法先获取 CursorId 的值，再配合对应的输入的 Pointer 的输入设备 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POINTER_INFO&lt;/code&gt; 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sourceDevice&lt;/code&gt; 字段，即可与初始化过程中创建的设备相关联，实现代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsValid&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;Info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerType&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;POINTER_INPUT_TYPE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PT_TOUCH&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;Info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerType&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;POINTER_INPUT_TYPE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PT_PEN&lt;/span&gt;&lt;span class=&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;n&quot;&gt;cursorId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;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;UnsafeNativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPointerCursorId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerId&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;cursorId&lt;/span&gt;&lt;span class=&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;deviceId&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;Info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sourceDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

                    &lt;span class=&quot;c1&quot;&gt;// If we cannot acquire the latest tablet and stylus then wait for the&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// next message.&lt;/span&gt;
                    &lt;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;UpdateCurrentTabletAndStylus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deviceId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cursorId&lt;/span&gt;&lt;span class=&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;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;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;在 WPF 初始化工作里面将输入的 Pointer 的输入设备 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POINTER_INFO&lt;/code&gt; 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sourceDevice&lt;/code&gt; 当成 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deviceId&lt;/code&gt; 的概念，即 TabletDevice 的 Id 值。而 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cursorId&lt;/code&gt; 则是对应 StylusDevice 的 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;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// Attempts to update the current stylus and tablet devices for the latest WM_POINTER message.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// Will attempt retries if the tablet collection is invalid or does not contain the proper ids.&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;deviceId&quot;&amp;gt;The id of the TabletDevice&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;cursorId&quot;&amp;gt;The id of the StylusDevice&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;True if successfully updated, false otherwise.&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;UpdateCurrentTabletAndStylus&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;deviceId&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;cursorId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_currentTabletDevice&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tablets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetByDeviceId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deviceId&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;n&quot;&gt;_currentTabletDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetStylusByCursorId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cursorId&lt;/span&gt;&lt;span class=&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;_currentTabletDevice&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;_currentStylusDevice&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;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;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;对应的 GetByDeviceId 方法的代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;System.Windows.Input.StylusPointer&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;/// Maintains a collection of pointer device information for currently installed pointer devices&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;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PointerTabletDeviceCollection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TabletDeviceCollection&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;/// Holds a mapping of TabletDevices from their WM_POINTER device id&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;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&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;PointerTabletDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_tabletDeviceMap&lt;/span&gt; &lt;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;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PointerTabletDevice&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;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// Retrieve the TabletDevice associated with the device id&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;deviceId&quot;&amp;gt;The device id&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;The TabletDevice associated with the device id&amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PointerTabletDevice&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetByDeviceId&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;deviceId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;PointerTabletDevice&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tablet&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;_tabletDeviceMap&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;deviceId&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;tablet&lt;/span&gt;&lt;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;tablet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对应的 GetStylusByCursorId 的代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;System.Windows.Input.StylusPointer&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 WM_POINTER based implementation of the TabletDeviceBase class.&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;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PointerTabletDevice&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TabletDeviceBase&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 mapping from StylusDevice id to the actual StylusDevice for quick lookup.&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;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&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;PointerStylusDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;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;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;uint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PointerStylusDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&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;/// Retrieves the StylusDevice associated with the cursor id.&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;cursorId&quot;&amp;gt;The id of the StylusDevice to retrieve&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;The StylusDevice associated with the id&amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PointerStylusDevice&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetStylusByCursorId&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;cursorId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;PointerStylusDevice&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;k&quot;&gt;null&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;TryGetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cursorId&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;stylus&lt;/span&gt;&lt;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;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;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;通过以上方式即可通过 PointerId 获取的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cursorId&lt;/code&gt; 进而获取到对应 WPF 里面的设备对象，进而拿到 WPF 里面的设备 Id 号。通过上文的描述也可以看到 PointerId 和 TouchDevice 等的 Id 是不一样的，但是之间有关联关系&lt;/p&gt;

&lt;p&gt;调用了 UpdateCurrentTabletAndStylus 的一个副作用就是同步更新了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_currentTabletDevice&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_currentStylusDevice&lt;/code&gt; 字段的值，后续逻辑即可直接使用这两个字段而不是传参数&lt;/p&gt;

&lt;p&gt;完成关联逻辑之后，即进入 GenerateRawStylusData 方法，这个方法是 WPF 获取 Pointer 具体的消息的核心方法，方法签名如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;System.Windows.Interop&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;/// Implements an input provider per hwnd for WM_POINTER messages&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;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HwndPointerInputProvider&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;IStylusInputProvider&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;/// Creates raw stylus data from the raw WM_POINTER properties&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;pointerData&quot;&amp;gt;The current pointer info&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;tabletDevice&quot;&amp;gt;The current TabletDevice&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;An array of raw pointer data&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;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GenerateRawStylusData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PointerData&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PointerTabletDevice&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;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;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;此 GenerateRawStylusData 被调用是这么写的&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;System.Windows.Interop&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;/// Implements an input provider per hwnd for WM_POINTER messages&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;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HwndPointerInputProvider&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;IStylusInputProvider&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;/// Processes the latest WM_POINTER message and forwards it to the WPF input stack.&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;pointerId&quot;&amp;gt;The id of the pointer message&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;action&quot;&amp;gt;The stylus action being done&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;timestamp&quot;&amp;gt;The time (in ticks) the message arrived&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;True if successfully processed (handled), false otherwise&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;ProcessMessage&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;pointerId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RawStylusActions&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;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;PointerData&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;PointerData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerId&lt;/span&gt;&lt;span class=&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;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cursorId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;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;UnsafeNativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPointerCursorId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerId&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;cursorId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&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;GenerateRawStylusData&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;_currentTabletDevice&lt;/span&gt;&lt;span class=&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;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;在 GenerateRawStylusData 方法里面，先通过 PointerTabletDevice 取出支持的 Pointer 的设备属性列表的长度，用于和输入点的信息进行匹配。回忆一下，这部分获取逻辑是在上文介绍到对 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerdeviceproperties&quot;&gt;GetPointerDeviceProperties&lt;/a&gt; 函数的调用提到的，且也说明了此函数拿到的设备属性列表的顺序是非常关键的，设备属性列表的顺序和在后续 WM_POINTER 消息拿到的裸数据的顺序是直接对应的&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;/// Implements an input provider per hwnd for WM_POINTER messages&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;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HwndPointerInputProvider&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;IStylusInputProvider&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;/// Creates raw stylus data from the raw WM_POINTER properties&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;pointerData&quot;&amp;gt;The current pointer info&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;tabletDevice&quot;&amp;gt;The current TabletDevice&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;An array of raw pointer data&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;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GenerateRawStylusData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PointerData&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PointerTabletDevice&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;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Since we are copying raw pointer data, we want to use every property supported by this pointer.&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// We may never access some of the unknown (unsupported by WPF) properties, but they should be there&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// for consumption by the developer.&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerPropertyCount&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;DeviceInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SupportedPointerProperties&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;// The data is as wide as the pointer properties and is per history point&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;rawPointerData&lt;/span&gt; &lt;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;pointerPropertyCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerData&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;historyCount&lt;/span&gt;&lt;span class=&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;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;由每个 Pointer 的属性长度配合总共的历史点数量，即可获取到这里面使用到的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rawPointerData&lt;/code&gt; 数组的长度。这部分代码相信大家很好就理解了&lt;/p&gt;

&lt;p&gt;接着就是核心部分，调用 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getrawpointerdevicedata&quot;&gt;GetRawPointerDeviceData&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;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerPropertyCount&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;DeviceInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SupportedPointerProperties&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;// The data is as wide as the pointer properties and is per history point&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;rawPointerData&lt;/span&gt; &lt;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;pointerPropertyCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerData&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;historyCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// Get the raw data formatted to our supported properties&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&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;GetRawPointerDeviceData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;pointerData&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;pointerId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;pointerData&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;historyCount&lt;/span&gt;&lt;span class=&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;pointerPropertyCount&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;DeviceInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SupportedPointerProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;rawPointerData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&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;在 Pointer 的设计里面，历史点 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;historyCount&lt;/code&gt; 是包含当前点的，且当前点就是最后一个点。这就是为什么这里只需要传入历史点数量即可，换句话说就是历史点最少包含一个点，那就是当前点&lt;/p&gt;

&lt;p&gt;由于 Pointer 获取到的点都是相对于屏幕坐标的，这里需要先偏移一下修改为窗口坐标系，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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 X and Y offsets to translate device coords to the origin of the hwnd&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originOffsetX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originOffsetY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;GetOriginOffsetsLogical&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;originOffsetX&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;originOffsetY&lt;/span&gt;&lt;span class=&quot;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;GetOriginOffsetsLogical&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;originOffsetX&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;originOffsetY&lt;/span&gt;&lt;span class=&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;n&quot;&gt;originScreenCoord&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;Value&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;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;c1&quot;&gt;// Use the inverse of our logical tablet to screen matrix to generate tablet coords&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;MatrixTransform&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;screenToTablet&lt;/span&gt; &lt;span class=&quot;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;_currentTabletDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TabletToScreen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;screenToTablet&lt;/span&gt; &lt;span class=&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;screenToTablet&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;n&quot;&gt;Point&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originTabletCoord&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originScreenCoord&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;screenToTablet&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;originOffsetX&lt;/span&gt; &lt;span class=&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;originTabletCoord&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;originOffsetY&lt;/span&gt; &lt;span class=&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;originTabletCoord&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;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// The HwndSource for WM_POINTER messages&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;SecurityCriticalDataClass&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;_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;p&gt;这里的 GetOriginOffsetsLogical 的实现逻辑就是取窗口的 0,0 点，看这个点会在屏幕的哪里，从而知道其偏移量。至于添加到 MatrixTransform 矩阵的 TabletToScreen 则在后文的具体转换逻辑会讲到，这里先跳过&lt;/p&gt;

&lt;p&gt;获取到相对于窗口的坐标偏移量之后，即可将其叠加给到每个点上，用于将这些点转换为窗口坐标系。但是在此之前还需要将获取到的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rawPointerData&lt;/code&gt; 进行加工。这一个步骤仅仅只是在 WPF 有需求，仅仅只是为了兼容 WISP 获取到的裸数据的方式。其相差点在于通过 Pointer 获取到的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rawPointerData&lt;/code&gt; 的二进制数据格式里面，没有带上按钮的支持情况的信息，在 WPF 这边需要重新创建一个数组对 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rawPointerData&lt;/code&gt; 重新排列，确保每个点的数据都加上按钮的信息数据&lt;/p&gt;

&lt;p&gt;这部分处理仅只是为了兼容考虑，让后续的 StylusPointCollection 开森而已，咱就跳着看就好了&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;numButtons&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;DeviceInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SupportedPointerProperties&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;tabletDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DeviceInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SupportedButtonPropertyIndex&lt;/span&gt;&lt;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;rawDataPointSize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numButtons&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;pointerPropertyCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numButtons&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;pointerPropertyCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

                &lt;span class=&quot;c1&quot;&gt;// Instead of a single entry for each button we use one entry for all buttons so reflect that in the raw data size&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;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rawDataPointSize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerData&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;historyCount&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;j&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawPointerData&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;pointerPropertyCount&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;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;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawDataPointSize&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;n&quot;&gt;pointerPropertyCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&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;rawPointerData&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;n&quot;&gt;data&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;rawDataPointSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                    &lt;span class=&quot;c1&quot;&gt;// Apply offsets from the origin to raw pointer data here&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;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StylusPointDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RequiredXIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originOffsetX&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;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StylusPointDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RequiredYIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originOffsetY&lt;/span&gt;&lt;span class=&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;c1&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;重新拷贝的过程，还将点的坐标更换成窗口坐标系，即以上的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data[i + StylusPointDescription.RequiredXIndex] -= originOffsetX;&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data[i + StylusPointDescription.RequiredYIndex] -= originOffsetY;&lt;/code&gt; 两个代码&lt;/p&gt;

&lt;p&gt;完成获取之后，就将获取到的裸数据给返回了，这就是 GenerateRawStylusData 的内容&lt;/p&gt;

&lt;p&gt;在 ProcessMessage 方法里面获取到 GenerateRawStylusData 返回的原始指针信息，即可将其给到 RawStylusInputReport 作为参数，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;// Generate a raw input to send to the input manager to start the event chain in PointerLogic&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Int32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawData&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GenerateRawStylusData&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;_currentTabletDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;RawStylusInputReport&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rsir&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RawStylusInputReport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;InputMode&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;timestamp&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;Value&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;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;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_currentTabletDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StylusPointDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;_currentTabletDevice&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;_currentStylusDevice&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;rawData&lt;/span&gt;&lt;span class=&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;n&quot;&gt;_currentStylusDevice&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;将创建的 RawStylusInputReport 更新到当前的设备，作为设备的最新的指针信息&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ProcessMessage&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;pointerId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RawStylusActions&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;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;PointerData&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;PointerData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerId&lt;/span&gt;&lt;span class=&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;_currentStylusDevice&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;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;Value&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;rsir&lt;/span&gt;&lt;span class=&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;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SecurityCriticalDataClass&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;_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;p&gt;且还加入到 InputManager 的 ProcessInput 里面，进入 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;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ProcessMessage&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;pointerId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RawStylusActions&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;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;PointerData&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;PointerData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerId&lt;/span&gt;&lt;span class=&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;_currentStylusDevice&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;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;Value&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;rsir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// Now send the input report&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;n&quot;&gt;UnsecureCurrent&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;irea&lt;/span&gt;&lt;span class=&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;在进入 InputManager 的 ProcessInput 调度消息之前，先看看 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_currentStylusDevice.Update&lt;/code&gt; 里面的对原始指针信息的解析实现逻辑&lt;/p&gt;

&lt;p&gt;在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_currentStylusDevice.Update&lt;/code&gt; 里面的对原始指针信息的解析实现完全是靠 StylusPointCollection 和 StylusPoint 的构造函数实现的&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;System.Windows.Input.StylusPointer&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 WM_POINTER specific implementation of the StylusDeviceBase.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// Supports direct access to WM_POINTER structures and basing behavior off of the WM_POINTER data.&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;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PointerStylusDevice&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StylusDeviceBase&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;/// Updates the internal StylusDevice state based on the WM_POINTER input and the formed raw data.&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;provider&quot;&amp;gt;The hwnd associated WM_POINTER provider&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;inputSource&quot;&amp;gt;The PresentationSource where this message originated&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;pointerData&quot;&amp;gt;The aggregated pointer data retrieved from the WM_POINTER stack&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;rsir&quot;&amp;gt;The raw stylus input generated from the pointer data&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;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;HwndPointerInputProvider&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&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;PointerData&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RawStylusInputReport&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rsir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&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;// First get the initial stylus points.  Raw data from pointer input comes in screen coordinates, keep that here since that is what we expect.&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_currentStylusPoints&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StylusPointCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rsir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StylusPointDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rsir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetRawPacketData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetTabletToElementTransform&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;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;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rsir.GetRawPacketData()&lt;/code&gt; 是返回上文提到的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GenerateRawStylusData&lt;/code&gt; 方法给出的裸数据的拷贝，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;RawStylusInputReport&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InputReport&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;///     Read-only access to the raw data that was reported.&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;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetRawPacketData&lt;/span&gt;&lt;span class=&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;_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;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;return&lt;/span&gt; &lt;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;_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Clone&lt;/span&gt;&lt;span class=&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;/// The raw data for this input report&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;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;_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;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;这里的 GetTabletToElementTransform 包含了一个核心转换，方法代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;PointerStylusDevice&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StylusDeviceBase&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 transform for converting from tablet to element&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;///     relative coordinates.&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;n&quot;&gt;GeneralTransform&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetTabletToElementTransform&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;relativeTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;GeneralTransformGroup&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;GeneralTransformGroup&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;toDevice&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;Value&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;TransformToDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;toDevice&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;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;nf&quot;&gt;MatrixTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PointerTabletDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TabletToScreen&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toDevice&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;StylusDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetElementTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relativeTo&lt;/span&gt;&lt;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;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;这里面方法存在重点内容，那就是 PointerTabletDevice 的 TabletToScreen 属性的计算方法。这个矩阵的计算需要用到开始初始化过程的 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerdevicerects&quot;&gt;GetPointerDeviceRects&lt;/a&gt; 函数获取 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;displayRect&lt;/code&gt; 尺寸，以及 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerdeviceproperties&quot;&gt;GetPointerDeviceProperties&lt;/a&gt; 获取的 X 和 Y 属性描述信息，属性的定义代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;n&quot;&gt;Matrix&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TabletToScreen&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;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_tabletInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SizeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ScreenSize&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;_tabletInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SizeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TabletSize&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;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;_tabletInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SizeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ScreenSize&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;_tabletInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SizeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TabletSize&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;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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以看到这是一个用于缩放的 Matrix 对象，正是 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerdevicerects&quot;&gt;GetPointerDeviceRects&lt;/a&gt; 获取的屏幕尺寸以及 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerdeviceproperties&quot;&gt;GetPointerDeviceProperties&lt;/a&gt; 获取的 X 和 Y 属性描述信息构成的 TabletSize 的比值&lt;/p&gt;

&lt;p&gt;回顾一下 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_tabletInfo&lt;/code&gt; 的 SizeInfo 的创建代码，可以看到 TabletSize 完全是由描述符的尺寸决定，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;// 以下代码在 PointerTabletDeviceInfo.cs 文件中&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// private bool TryInitializeSupportedStylusPointProperties()&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;SupportedPointerProperties&lt;/span&gt; &lt;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;UnsafeNativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;POINTER_DEVICE_PROPERTY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;propCount&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;n&quot;&gt;UnsafeNativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPointerDeviceProperties&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;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SupportedPointerProperties&lt;/span&gt;&lt;span class=&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;// private bool TryInitializeDeviceRects()&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deviceRect&lt;/span&gt; &lt;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;UnsafeNativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;displayRect&lt;/span&gt; &lt;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;UnsafeNativeMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&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;success&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;GetPointerDeviceRects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_deviceInfo&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;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deviceRect&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;displayRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;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;// We use the max X and Y properties here as this is more readily useful for raw data&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// which is where all conversions come from.&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;SizeInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TabletDeviceSizeInfo&lt;/span&gt;
                &lt;span class=&quot;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;n&quot;&gt;SupportedPointerProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StylusPointDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RequiredXIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logicalMax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;SupportedPointerProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StylusPointDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RequiredYIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logicalMax&lt;/span&gt;&lt;span class=&quot;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;n&quot;&gt;displayRect&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;displayRect&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;displayRect&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;displayRect&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;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TabletDeviceSizeInfo&lt;/span&gt;
    &lt;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;Size&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TabletSize&lt;/span&gt;&lt;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;Size&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ScreenSize&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;TabletDeviceSizeInfo&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;tabletSize&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;screenSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;TabletSize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tabletSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ScreenSize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;screenSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如此即可使用 TabletToScreen 属性将收到的基于 Tablet 坐标系的裸指针消息的坐标转换为屏幕坐标，再配合 TransformToDevice 取反即可转换到 WPF 坐标系&lt;/p&gt;

&lt;p&gt;在以上代码里面，由于传入 GetTabletToElementTransform 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;relativeTo&lt;/code&gt; 参数是 null 的值，将导致 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StylusDevice.GetElementTransform(relativeTo)&lt;/code&gt; 返回一个单位矩阵，这就意味着在 GetTabletToElementTransform 方法里面的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;group.Children.Add(StylusDevice.GetElementTransform(relativeTo));&lt;/code&gt; 是多余的，也许后续 WPF 版本这里会被我优化掉&lt;/p&gt;

&lt;p&gt;回顾一下 StylusPointCollection 的构造函数参数，有用的参数只有前三个，分别是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rsir.StylusPointDescription&lt;/code&gt; 传入描述符信息，以及 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rsir.GetRawPacketData()&lt;/code&gt; 返回裸指针数据，以及 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetTabletToElementTransform(null)&lt;/code&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;_currentStylusPoints&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StylusPointCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rsir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StylusPointDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rsir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetRawPacketData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetTabletToElementTransform&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那 StylusPointCollection 的最后一个参数，即上述代码传入的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Matrix.Identity&lt;/code&gt; 有什么用途？其实在 StylusPointCollection 的设计里面，第三个参数和第四个参数是二选一的，且第三个参数的优先级大于第四个参数。即在 StylusPointCollection 底层会判断第三个参数是否有值，如果没有值才会使用第四个参数&lt;/p&gt;

&lt;p&gt;在 StylusPointCollection 构造函数里面将会对裸 Pointer 数据进行处理，现在 GetRawPacketData 拿到的裸 Pointer 数据的 int 数组里面的数据排列内容大概如下&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;| X 坐标 | Y 坐标 | 压感（可选）| StylusPointDescription 里面的属性列表一一对应 |
| X 坐标 | Y 坐标 | 压感（可选）| StylusPointDescription 里面的属性列表一一对应 |
| X 坐标 | Y 坐标 | 压感（可选）| StylusPointDescription 里面的属性列表一一对应 |
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;存放的是一个或多个点信息，每个点的信息都是相同的二进制长度，分包非常简单&lt;/p&gt;

&lt;p&gt;进入到 StylusPointCollection 的构造函数，看看其代码签名定义&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;System.Windows.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;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StylusPointCollection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Collection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StylusPoint&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;internal&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StylusPointCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StylusPointDescription&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stylusPointDescription&lt;/span&gt;&lt;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;rawPacketData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GeneralTransform&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tabletToView&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;tabletToViewMatrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&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;在构造函数里面，先调用 StylusPointDescription 的 GetInputArrayLengthPerPoint 方法，获取每个点的二进制长度，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;StylusPointCollection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Collection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StylusPoint&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;internal&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StylusPointCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StylusPointDescription&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stylusPointDescription&lt;/span&gt;&lt;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;rawPacketData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GeneralTransform&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tabletToView&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;tabletToViewMatrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&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;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lengthPerPoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stylusPointDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetInputArrayLengthPerPoint&lt;/span&gt;&lt;span class=&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;language-plaintext highlighter-rouge&quot;&gt;rawPacketData&lt;/code&gt; 参数包含多少个点的信息&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;nf&quot;&gt;StylusPointCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StylusPointDescription&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stylusPointDescription&lt;/span&gt;&lt;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;rawPacketData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GeneralTransform&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tabletToView&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;tabletToViewMatrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&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;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lengthPerPoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stylusPointDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetInputArrayLengthPerPoint&lt;/span&gt;&lt;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;logicalPointCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawPacketData&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;lengthPerPoint&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;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawPacketData&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;lengthPerPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Invalid assumption about packet length, there shouldn&apos;t be any remainder&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上代码的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Debug.Assert&lt;/code&gt; 就是要确保传入的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rawPacketData&lt;/code&gt; 是可以被 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lengthPerPoint&lt;/code&gt; 即每个点的二进制长度所整除&lt;/p&gt;

&lt;p&gt;完成准备工作之后，接下来就可以将 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rawPacketData&lt;/code&gt; 解出点了，如下面代码所示&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;lengthPerPoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stylusPointDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetInputArrayLengthPerPoint&lt;/span&gt;&lt;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;logicalPointCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawPacketData&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;lengthPerPoint&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;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;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;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logicalPointCount&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;n&quot;&gt;lengthPerPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;//first, determine the x, y values by xf-ing them&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Point&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;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;rawPacketData&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;rawPacketData&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;c1&quot;&gt;// 忽略其他代码&lt;/span&gt;

                &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startIndex&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;c1&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;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;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dataLength&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lengthPerPoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dataLength&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;c1&quot;&gt;//copy the rest of the data&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawArrayStartIndex&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;startIndex&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;rawPacketData&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;n&quot;&gt;rawArrayStartIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dataLength&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;n&quot;&gt;StylusPoint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newPoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StylusPoint&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;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;StylusPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DefaultPressure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_stylusPointDescription&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;false&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;// 忽略其他代码&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;n&quot;&gt;StylusPoint&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;Items&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;newPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上代码忽略的部分包含了一些细节，如对 Point 的坐标转换，使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Point p = new Point(rawPacketData[i], rawPacketData[i + 1]);&lt;/code&gt; 拿到的点的坐标是属于 Tablet 坐标，需要使用传入的参数转换为 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;internal&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StylusPointCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StylusPointDescription&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stylusPointDescription&lt;/span&gt;&lt;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;rawPacketData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GeneralTransform&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tabletToView&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;tabletToViewMatrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&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;Point&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;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;rawPacketData&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;rawPacketData&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tabletToView&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;tabletToView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryTransform&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;k&quot;&gt;out&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;else&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;tabletToViewMatrix&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;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;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;通过以上的代码就可以看到 StylusPointCollection 构造函数使用了第三个或第四个参数作为变换，如果第三个参数存在则优先使用第三个参数&lt;/p&gt;

&lt;p&gt;其他处理的逻辑就是对压感的额外处理，压感作为 StylusPoint 的一个明确参数，需要额外判断处理&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;startIndex&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;// X 和 Y 占用了两个元素&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;containsTruePressure&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stylusPointDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContainsTruePressure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;containsTruePressure&lt;/span&gt;&lt;span class=&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&apos;t copy pressure in the int[] for extra data&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;startIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;n&quot;&gt;StylusPoint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newPoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StylusPoint&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;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;StylusPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DefaultPressure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_stylusPointDescription&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;false&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;containsTruePressure&lt;/span&gt;&lt;span class=&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;//use the algorithm to set pressure in StylusPoint&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pressure&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawPacketData&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;n&quot;&gt;newPoint&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;StylusPointProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NormalPressure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pressure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如此即可解包 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;| X 坐标 | Y 坐标 | 压感（可选）| StylusPointDescription 里面的属性列表一一对应 |&lt;/code&gt; 里面前三个元素，其中压感是可选的。后续的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StylusPointDescription 里面的属性列表一一对应&lt;/code&gt; 部分需要重新创建 data 数组传入到各个 StylusPoint 里面，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;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;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dataLength&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lengthPerPoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dataLength&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;c1&quot;&gt;//copy the rest of the data&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawArrayStartIndex&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;startIndex&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;rawPacketData&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;n&quot;&gt;rawArrayStartIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dataLength&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;后续对 StylusPoint 获取属性时，即可通过描述信息获取，描述信息获取到值的方式就是取以上代码传入的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data&lt;/code&gt; 二进制数组的对应下标的元素，比如触摸点的宽度或高度信息&lt;/p&gt;

&lt;p&gt;完成转换为 StylusPointCollection 之后，即可使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InputManager.UnsecureCurrent.ProcessInput&lt;/code&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;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ProcessMessage&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;pointerId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RawStylusActions&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;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&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;InputReportEventArgs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;irea&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InputReportEventArgs&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;n&quot;&gt;StylusDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rsir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&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;InputManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PreviewInputReportEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

                    &lt;span class=&quot;c1&quot;&gt;// Now send the input report&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;n&quot;&gt;UnsecureCurrent&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;irea&lt;/span&gt;&lt;span class=&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;进入到 ProcessInput 里面将会走标准的路由事件机制，通过路由机制触发 Touch 或 Stylus 事件，接下来的逻辑看一下调用堆栈即可，和其他的输入事件逻辑差不多&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;&amp;gt;   Lindexi.dll!Lindexi.MainWindow.MainWindow_TouchDown(object sender, System.Windows.Input.TouchEventArgs e)
    PresentationCore.dll!System.Windows.RoutedEventArgs.InvokeHandler(System.Delegate handler, object target)
    PresentationCore.dll!System.Windows.EventRoute.InvokeHandlersImpl(object source, System.Windows.RoutedEventArgs args, bool reRaised) 
    PresentationCore.dll!System.Windows.UIElement.RaiseEventImpl(System.Windows.DependencyObject sender, System.Windows.RoutedEventArgs args)
    PresentationCore.dll!System.Windows.UIElement.RaiseTrustedEvent(System.Windows.RoutedEventArgs args) 
    PresentationCore.dll!System.Windows.Input.InputManager.ProcessStagingArea()
    PresentationCore.dll!System.Windows.Input.TouchDevice.RaiseTouchDown() 
    PresentationCore.dll!System.Windows.Input.TouchDevice.ReportDown() 
    PresentationCore.dll!System.Windows.Input.StylusTouchDeviceBase.OnDown() 
    PresentationCore.dll!System.Windows.Input.StylusPointer.PointerLogic.PromoteMainDownToTouch(System.Windows.Input.StylusPointer.PointerStylusDevice stylusDevice, System.Windows.Input.StagingAreaInputItem stagingItem)
    PresentationCore.dll!System.Windows.Input.InputManager.RaiseProcessInputEventHandlers(System.Tuple&amp;lt;System.Windows.Input.ProcessInputEventHandler, System.Delegate[]&amp;gt; postProcessInput, System.Windows.Input.ProcessInputEventArgs processInputEventArgs) 
    PresentationCore.dll!System.Windows.Input.InputManager.ProcessStagingArea()
    PresentationCore.dll!System.Windows.Interop.HwndPointerInputProvider.ProcessMessage(uint pointerId, System.Windows.Input.RawStylusActions action, int timestamp) 
    PresentationCore.dll!System.Windows.Interop.HwndPointerInputProvider.System.Windows.Interop.IStylusInputProvider.FilterMessage(nint hwnd, MS.Internal.Interop.WindowMessage msg, nint wParam, nint lParam, ref bool handled) 
    PresentationCore.dll!System.Windows.Interop.HwndSource.InputFilterMessage(nint hwnd, int msg, nint wParam, nint lParam, ref bool handled)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;由于我跑的是 Release 版本的 WPF 导致了有一些函数被内联，如从 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HwndPointerInputProvider.ProcessMessage&lt;/code&gt; 到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InputManager.ProcessStagingArea&lt;/code&gt; 中间就少了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InputManager.ProcessInput&lt;/code&gt; 函数，完全的无函数内联的堆栈应该如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;PresentationCore&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;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;n&quot;&gt;PresentationCore&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;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;PresentationCore&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;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;Interop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HwndPointerInputProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ProcessMessage&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;pointerId&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;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RawStylusActions&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;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如下面代码是 ProcessInput 函数的代码&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;InputManager&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;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&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;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;PushMarker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;PushInput&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;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;RequestContinueProcessingStagingArea&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;handled&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;k&quot;&gt;return&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;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;进入到 ProcessStagingArea 方法会执行具体的调度逻辑，用上述触摸按下的堆栈作为例子，将会进入到 PointerLogic 的 PostProcessInput 方法里面，由 PostProcessInput 方法调用到 PromoteMainToOther 再到 PromoteMainToTouch 最后到 PromoteMainDownToTouch 方法。只不过中间的几个方法被内联了，直接从堆栈上看就是从 RaiseProcessInputEventHandlers 到 PromoteMainDownToTouch 方法，堆栈如下&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;PresentationCore.dll!System.Windows.Input.StylusPointer.PointerLogic.PromoteMainDownToTouch(...)
PresentationCore.dll!System.Windows.Input.InputManager.RaiseProcessInputEventHandlers(...)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;核心触发按下的代码就在 PromoteMainDownToTouch 里，其代码大概如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;PromoteMainDownToTouch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PointerStylusDevice&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;StagingAreaInputItem&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;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;PointerTouchDevice&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;touchDevice&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;TouchDevice&lt;/span&gt;&lt;span class=&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;touchDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OnActivate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;touchDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OnDown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;从上文可以知道，在 HwndPointerInputProvider 的 ProcessMessage 里面调用了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_currentStylusDevice.Update&lt;/code&gt; 方法时，就将输入的数据存放到 PointerStylusDevice 里面&lt;/p&gt;

&lt;p&gt;后续的逻辑就和 &lt;a href=&quot;/post/WPF-%E6%A8%A1%E6%8B%9F%E8%A7%A6%E6%91%B8%E8%AE%BE%E5%A4%87.html&quot;&gt;WPF 模拟触摸设备&lt;/a&gt; 提到的使用方法差不多，只是数据提供源是从 PointerStylusDevice 提供。如果大家对进入到 InputManager 的后续逻辑感兴趣，可参考 &lt;a href=&quot;/post/WPF-%E9%80%9A%E8%BF%87-InputManager-%E6%A8%A1%E6%8B%9F%E8%B0%83%E5%BA%A6%E8%A7%A6%E6%91%B8%E4%BA%8B%E4%BB%B6.html&quot;&gt;WPF 通过 InputManager 模拟调度触摸事件&lt;/a&gt; 提供的方法自己跑一下&lt;/p&gt;

&lt;p&gt;在 WPF 里面，除了走 WM_Pointer 之外，还有默认的 RealTimeStylus 机制。根据 &lt;a href=&quot;https://blog.sdlsj.net&quot;&gt;lsj&lt;/a&gt; 大佬的考古，预计在 Win10 的版本下，底层的 RealTimeStylus 机制的数据也是从 WM_Pointer 里面来的，如下图所示&lt;/p&gt;

&lt;!-- ![](image/WPF 从零自己实现从 RealTimeStylus 获取触摸信息/WPF 从零自己实现从 RealTimeStylus 获取触摸信息3.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20233271731347622.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;以上的 581 和 582 等就是对应的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WM_Pointer&lt;/code&gt; 消息号。更多细节信息请参阅 &lt;a href=&quot;/post/WPF-%E4%BB%8E%E9%9B%B6%E8%87%AA%E5%B7%B1%E5%AE%9E%E7%8E%B0%E4%BB%8E-RealTimeStylus-%E8%8E%B7%E5%8F%96%E8%A7%A6%E6%91%B8%E4%BF%A1%E6%81%AF.html&quot;&gt;WPF 从零自己实现从 RealTimeStylus 获取触摸信息&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;更多触摸请看 &lt;a href=&quot;/post/WPF-%E8%A7%A6%E6%91%B8%E7%9B%B8%E5%85%B3.html&quot;&gt;WPF 触摸相关&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 07 Apr 2026 22:01:27 +0000</pubDate>
        <link>/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-%E4%BB%8E-WM_POINTER-%E6%B6%88%E6%81%AF%E5%88%B0-Touch-%E4%BA%8B%E4%BB%B6.html</link>
        <guid isPermaLink="true">/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-%E4%BB%8E-WM_POINTER-%E6%B6%88%E6%81%AF%E5%88%B0-Touch-%E4%BA%8B%E4%BB%B6.html</guid>
        
        
        <category>WPF</category>
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>dotnet 源代码生成器分析器入门</title>
        <description>&lt;p&gt;本文将带领大家入门 dotnet 的 SourceGenerator 源代码生成器技术，期待大家阅读完本文能够看懂理解和编写源代码生成器和分析器&lt;/p&gt;

&lt;!--more--&gt;

&lt;!-- CreateTime:2025/02/11 07:29:34 --&gt;
&lt;!-- 发布 --&gt;
&lt;!-- 博客 --&gt;
&lt;!-- 置顶1 --&gt;

&lt;p&gt;恭喜你看到了本文，进入到 C# dotnet 的深水区。如果你还是在浅水玩耍的小鲜肉，推荐你点击右上方的关闭按钮，避免受到过于深入的知识的污染&lt;/p&gt;

&lt;p&gt;我所在的团队在 Rosyln 刚出来没两年就开始玩了，那时候还没有现在这么多机制。我之前很多关于 Rosyln 的博客都涉及到了很底层的玩法，导致入门门槛过高。随着 dotnet 生态的不断建设，渐渐有了源代码生成技术、增量源代码生成技术等等。这次我打算综合之前的经验和知识，根据现在的 dotnet 的生态技术，编写这篇入门博客，让大家更好地入门源代码生成器和分析器，降低入门门槛。本文将尽量使用比较缓的知识爬坡方式编写，以便让大家更舒适地进入到源代码生成器和分析器的世界&lt;/p&gt;

&lt;p&gt;在开始之前期望大家已经了解基础的 dotnet C# 基础知识，了解基础的概念和项目组织结构&lt;/p&gt;

&lt;p&gt;在阅读本文过程中，发现本文有任何错误或不足之处，欢迎大家在评论区留言或发送邮件给我，我会尽快修正。如果大家有任何问题或疑问，也欢迎大家在评论区留言或发送邮件给我，我会尽快回复&lt;/p&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;本文先从项目搭建开始告诉大家如何创建一个源代码生成器项目。本文后续的内容将会在这个项目中进行演示。本文的编写顺序是先搭建项目，然后再讲解一些基础的概念和用法，再到如何进行调试，最后提供一些实际的演练给到大家。基础知识部分也放在演练里面，先做演练再讲基础知识，防止一口气拍出大量基础知识劝退大家&lt;/p&gt;

&lt;p&gt;本文的推荐打开方式是一边阅读本文，一边打开 Visual Studio 2022 或更高版本，对照本文的内容进行操作。照着本文的内容对照着编写代码，可以让大家更好地理解本文的内容，照着过一遍预计就能掌握基础的源代码生成器和分析器的知识，入门源代码生成器和分析器的编写&lt;/p&gt;

&lt;p&gt;本文过程中会添加一些外部链接文档，这些外部链接文档都是可选阅读内容，只供大家感兴趣时扩展阅读。本文的核心内容是在本文中编写的，不需要阅读外部链接文档也能够掌握本文的内容。作为入门博客，我担心自己编写过程中存在高手盲区问题，于是尽可能将更多细节写出来，尽管这样会导致一些重复的表述&lt;/p&gt;

&lt;p&gt;先新建一个控制台项目，新建完成之后在 Visual Studio 2022 或更高版本中打开项目，双击 csproj 项目文件，即可进行编辑项目文件&lt;/p&gt;

&lt;p&gt;本文这里新建了一个名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DercelgefarKarhelchaye.Analyzer&lt;/code&gt; 的控制台项目。也许细心的伙伴发现了这个项目使用了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Analyzer&lt;/code&gt; 作为后缀，这是因为在 dotnet 中源代码生成器和分析器是一体的，按照历史原因的惯性，依然将其命名为分析器项目。在 Visual Studio 2022 的每个项目依赖项里面，大家都会看到如下图的一个名为分析器的项，而没有专门一个名为源代码生成器的项，其原因也是如此&lt;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门12.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20253414882001.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果在这一步就开始卡住了也不用慌，本文在整个过程中都会给出示例代码。我整个代码仓库比较庞大，使用本文各个部分提供的拉取源代码的命令行代码，可以减少拉取的数据，提升拉取的速度，且能够确保切换到正确的 commit 代码&lt;/p&gt;

&lt;p&gt;创建之后，在 Visual Studio 的解决方案里的界面大概如下&lt;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门7.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2025228201392285.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;编辑名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DercelgefarKarhelchaye.Analyzer&lt;/code&gt; 的控制台项目的 csproj 项目文件，将其 TargetFramework 降级到 netstandard2.0 版本，且按照 dotnet 的惯例，使用 NuGet 添加必要的组件。编辑之后的 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;LangVersion&amp;gt;&lt;/span&gt;latest&lt;span class=&quot;nt&quot;&gt;&amp;lt;/LangVersion&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;EnforceExtendedAnalyzerRules&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/EnforceExtendedAnalyzerRules&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.Analyzers&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3.11.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;Microsoft.CodeAnalysis.CSharp&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.12.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;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;为什么需要降级为 netstandard2.0 版本？这是为了让此分析器项目能够同时在 dotnet CLI 和 Visual Studio 2022 里面使用。在 Visual Studio 2022 里，当前依然使用的是 .NET Framework 的版本。于是求最小公倍数，选择了 netstandard2.0 版本。预计后续版本才能使用到最新的 dotnet 框架版本&lt;/p&gt;

&lt;p&gt;由于分析器本身需要利用到部分 VisualStudio 的基础设施支持，为了能够在 VisualStudio 里面更好地工作，某些情况下需要关注 Microsoft.CodeAnalysis.CSharp 库的版本，尽管大部分情况下都不应该惯着开发者，直接有多新就用多新，详细请参阅 &lt;a href=&quot;https://learn.microsoft.com/zh-cn/visualstudio/extensibility/roslyn-version-support?view=vs-2022&quot;&gt;支持的 Roslyn 包版本映射 - Visual Studio (Windows) - Microsoft Learn&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;以上的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;LangVersion&amp;gt;latest&amp;lt;/LangVersion&amp;gt;&lt;/code&gt; 只是为了方便让咱使用最新的语言特性。前面选择的 netstandard2.0 会导致语言特性默认开得比较低，这里设置为 latest 可以让我们使用最新的语言特性，让代码编写更加方便。这里需要再次提醒，在 dotnet 里面，语言和框架是分开的。使用低版本框架也能使用高版本语言。如果对语言和框架的关系依然有所疑惑，推荐先了解一下 dotnet 的基础知识，不要着急往下看。编写源代码生成器和分析器需要对 dotnet 有一定的了解，否则写着就开始混淆概念了&lt;/p&gt;

&lt;p&gt;以上的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;EnforceExtendedAnalyzerRules&amp;gt;true&amp;lt;/EnforceExtendedAnalyzerRules&amp;gt;&lt;/code&gt; 的作用是强制执行扩展分析器规则。这个属性是为了让我们在编写分析器的时候能够更加严格，让我们的代码更加规范。这里大家不需要细致了解，如有兴趣，请参阅 &lt;a href=&quot;/post/Roslyn-%E5%88%86%E6%9E%90%E5%99%A8-EnforceExtendedAnalyzerRules-%E5%B1%9E%E6%80%A7%E7%9A%84%E4%BD%9C%E7%94%A8.html&quot;&gt;Roslyn 分析器 EnforceExtendedAnalyzerRules 属性的作用&lt;/a&gt;
&lt;!-- [Roslyn 分析器 EnforceExtendedAnalyzerRules 属性的作用 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/17678673.html ) --&gt;&lt;/p&gt;

&lt;p&gt;以上的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.CodeAnalysis.Analyzers&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.CodeAnalysis.CSharp&lt;/code&gt; 是必须的组件。&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.CodeAnalysis.Analyzers&lt;/code&gt; 是分析器的基础组件，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.CodeAnalysis.CSharp&lt;/code&gt; 是 C# 的基础组件。这两个组件是必须的，没有这两个组件，我们就无法编写分析器和源代码生成器&lt;/p&gt;

&lt;p&gt;通过以上的步骤也可以让大家看到，其实 dotnet 分析器项目也没什么特殊的，依然可以通过一个简单的控制台项目修改而来。其核心关键仅仅只是安装了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.CodeAnalysis.Analyzers&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.CodeAnalysis.CSharp&lt;/code&gt; 两个组件而已&lt;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门8.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20252282020203474.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;现在只是有了一个空的分析器项目，但是还不知道这个项目的效果。为了让分析器项目工作，那就需要有一个被分析的项目。为此咱就再次新建一个控制台项目，让这个控制台项目成为被分析项目&lt;/p&gt;

&lt;p&gt;我这里新建了一个名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DercelgefarKarhelchaye&lt;/code&gt; 的控制台项目。编辑 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DercelgefarKarhelchaye&lt;/code&gt; 的 csproj 项目文件，让其引用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DercelgefarKarhelchaye.Analyzer&lt;/code&gt; 项目，且设置 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DercelgefarKarhelchaye.Analyzer&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;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;net9.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;ImplicitUsings&amp;gt;&lt;/span&gt;enable&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ImplicitUsings&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Nullable&amp;gt;&lt;/span&gt;enable&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Nullable&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;ProjectReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\DercelgefarKarhelchaye.Analyzer\DercelgefarKarhelchaye.Analyzer.csproj&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;OutputItemType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Analyzer&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;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;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DercelgefarKarhelchaye.Analyzer.csproj&lt;/code&gt; 的引用上。且和正常的引用项目的方式不同的是，这里额外添加了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OutputItemType=&quot;Analyzer&quot; ReferenceOutputAssembly=&quot;false&quot;&lt;/code&gt; 两个配置。这两个配置的作用如下：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;以上的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OutputItemType=&quot;Analyzer&quot;&lt;/code&gt; 是告诉 dotnet 这个引用项目是一个分析器项目。这个配置是必须的，没有这个配置，dotnet 就不知道这个项目是一个分析器项目。通过这个配置是告诉 dotnet 这个项目是一个分析器项目，才能让 dotnet 在编译的时候能够正确地当成分析器处理这个项目&lt;/li&gt;
  &lt;li&gt;以上的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReferenceOutputAssembly=&quot;false&quot;&lt;/code&gt; 是告诉 dotnet 不要引用这个项目的输出程序集。正常的项目是不应该引用分析器项目的程序集的，分析器项目的作用仅仅只是作为分析器，而不是提供程序集给其他项目引用。这个配置是为了让 dotnet 在编译的时候不要引用这个项目的输出程序集，避免引用错误或导致不小心用了不应该使用的类型&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;对于正常的项目引用来说，一旦存在项目引用，那被引用的项目的输出程序集就会被引用。此时项目上就可以使用被引用项目的公开类型，以及获取 NuGet 包依赖传递等。但是对于分析器项目来说，这些都是不应该的，正常就不能让项目引用分析器项目的输出程序集。这就是为什么会额外添加 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReferenceOutputAssembly=&quot;false&quot;&lt;/code&gt; 配置的原因&lt;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门9.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20252282020588315.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在这里，咱接触到了非常多次的 csproj 项目文件，如果大家对 csproj 项目文件格式感兴趣，请参阅 &lt;a href=&quot;https://blog.walterlv.com/post/understand-the-csproj&quot;&gt;理解 C# 项目 csproj 文件格式的本质和编译流程 - walterlv&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;以上的步骤完成之后，最简单的分析器项目和被分析的项目就搭建完成了。这也是分析器的基础，大部分的带分析器的代码都是如此方式搭建的。但也有其他部分是通过 NuGet 带出去的分析器，被 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;language-plaintext highlighter-rouge&quot;&gt;DercelgefarKarhelchaye.Analyzer&lt;/code&gt; 项目中新建一个名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalGenerator&lt;/code&gt; 的源代码生成器类。编辑 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalGenerator&lt;/code&gt; 类，让其继承 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IIncrementalGenerator&lt;/code&gt; 接口，实现 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Initialize&lt;/code&gt; 方法，且标记 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[Generator(LanguageNames.CSharp)]&lt;/code&gt; 特性。编辑之后的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalGenerator&lt;/code&gt; 类的内容如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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.CodeAnalysis&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;DercelgefarKarhelchaye.Analyzer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Generator&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;IncrementalGenerator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IIncrementalGenerator&lt;/span&gt;
&lt;span class=&quot;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;IncrementalGeneratorInitializationContext&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;// 忽略其他代码&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;本文这里直接就是和大家介绍 IIncrementalGenerator 增量 Source Generator 源代码生成器技术，不再介绍 ISourceGenerator 源代码生成器技术。其原因是在 2022 之后，官方大力推荐的是使用 IIncrementalGenerator 增量源代码生成器技术。从业务上讲，仅仅只是 IIncrementalGenerator 多了增量的功能，在进行源代码生成逻辑处理中没有太大的差别。功能上 IIncrementalGenerator 也能完全代替 ISourceGenerator 的功能。但是在性能上，IIncrementalGenerator 要比 ISourceGenerator 更加高效，更加快速，更加能够防止原本已经很卡的 Visual Studio 更加卡&lt;/p&gt;

&lt;p&gt;整个 IIncrementalGenerator 的入口都在 Initialize 方法里面，从 IncrementalGeneratorInitializationContext 参数里可以点出来非常多有用的方法。咱这里先不展开讲解这些方法，先让大家看到一个简单的源代码生成器的效果&lt;/p&gt;

&lt;p&gt;在 Initialize 方法里面，咱可以通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;context.RegisterPostInitializationOutput&lt;/code&gt; 方法注册一个源代码输出。如以下代码所示，将输出一个名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GeneratedCode&lt;/code&gt; 的代码&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;Initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IncrementalGeneratorInitializationContext&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;RegisterPostInitializationOutput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;initializationContext&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;initializationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;GeneratedCode.cs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;
&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;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;DercelgefarKarhelchaye&lt;/span&gt;
                &lt;span class=&quot;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;GeneratedCode&lt;/span&gt;
                    &lt;span class=&quot;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;{&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 from generated code!&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;s&quot;&gt;&quot;&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;language-plaintext highlighter-rouge&quot;&gt;GeneratedCode&lt;/code&gt; 类型注入到被分析的项目中。在被分析的项目中，可以通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GeneratedCode.Print()&lt;/code&gt; 方法输出 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hello from generated code!&lt;/code&gt; 字符串&lt;/p&gt;

&lt;p&gt;好的，进入到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DercelgefarKarhelchaye&lt;/code&gt; 项目中，编辑 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Program&lt;/code&gt; 类，调用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GeneratedCode.Print()&lt;/code&gt; 方法。编辑之后的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Program&lt;/code&gt; 类的内容如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;DercelgefarKarhelchaye&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;GeneratedCode&lt;/span&gt;&lt;span class=&quot;p&quot;&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;尝试运行一下 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DercelgefarKarhelchaye&lt;/code&gt; 项目，可以看到控制台输出了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hello from generated code!&lt;/code&gt; 字符串。这就是源代码生成器的效果，通过源代码生成器生成的代码，注入到被分析的项目中，让被分析的项目能够使用生成的代码&lt;/p&gt;

&lt;p&gt;如此证明了在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DercelgefarKarhelchaye.Analyzer&lt;/code&gt; 分析器项目中编写的源代码生成器生效了。这就是源代码生成器的基硋，通过源代码生成器生成的代码，注入到被分析的项目中，让被分析的项目能够使用生成的代码&lt;/p&gt;

&lt;p&gt;以上代码放在 &lt;a href=&quot;https://github.com/lindexi/lindexi_gd/tree/95c14524130238b2d6fbca97ca35b89dc921536b/Roslyn/DercelgefarKarhelchaye&quot;&gt;github&lt;/a&gt; 和 &lt;a href=&quot;https://gitee.com/lindexi/lindexi_gd/tree/95c14524130238b2d6fbca97ca35b89dc921536b/Roslyn/DercelgefarKarhelchaye&quot;&gt;gitee&lt;/a&gt; 上，可以使用如下命令行拉取代码。我整个代码仓库比较庞大，使用以下命令行可以进行部分拉取，拉取速度比较快&lt;/p&gt;

&lt;p&gt;先创建一个空文件夹，接着使用命令行 cd 命令进入此空文件夹，在命令行里面输入以下代码，即可获取到本文的代码&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;git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 95c14524130238b2d6fbca97ca35b89dc921536b
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上使用的是国内的 gitee 的源，如果 gitee 不能访问，请替换为 github 的源。请在命令行继续输入以下代码，将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码，可以发邮件向我要代码&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;git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 95c14524130238b2d6fbca97ca35b89dc921536b
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;获取代码之后，进入 Roslyn/DercelgefarKarhelchaye 文件夹，即可获取到源代码&lt;/p&gt;

&lt;h2 id=&quot;分析和生成入门&quot;&gt;分析和生成入门&lt;/h2&gt;

&lt;p&gt;在上文中，和大家介绍了如何生成静态的固定的代码内容。在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RegisterPostInitializationOutput&lt;/code&gt; 方法里面，只允许传递静态固定的代码，不能依据当前项目状态或配置进行动态生成代码。这是因为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RegisterPostInitializationOutput&lt;/code&gt; 方法的定义上就是用于提供分析器开始分析工作之前的初始化代码。这部分代码由于可不用运行分析过程，可以非常快给到 IDE 层，一般用于提供一些类型定义，可以给到开发者直接快速使用，而不会在使用过程中飘红&lt;/p&gt;

&lt;p&gt;上文的代码只是让大家粗略熟悉了一下 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IIncrementalGenerator&lt;/code&gt; 的 API 调用方法。接下来我将带大家开始入门分析器的分析和生成功能&lt;/p&gt;

&lt;p&gt;分析和生成很多时候都是不分离的，生成的代码需要依赖分析的结果。为了能让大家更好理解分析器的入门知识，我尝试布置一个任务，接下来让咱根据布置的任务来入门分析和生成功能&lt;/p&gt;

&lt;h3 id=&quot;任务&quot;&gt;任务&lt;/h3&gt;

&lt;p&gt;咱来实现一个经典的需求任务，将项目里面的标记了某个 Attribute 特性的类型全收集起来，最后生成一个代码，让生成的代码输出有哪些类型标记了这个 Attribute 特性，将这些类型的名称输出到控制台&lt;/p&gt;

&lt;p&gt;进一步分解任务需求，咱需要有一个源代码生成器。源代码生成器生成两部分代码，第一部分就是 FooAttribute 特性，第二部分就是收集所有标记了 FooAttribute 特性的类型，生成将这些类型的名称输出到控制台的代码。要求全程没有反射参与，全程都是通过 Roslyn 分析和生成完成&lt;/p&gt;

&lt;h3 id=&quot;使用-forattributewithmetadataname-快速分析代码&quot;&gt;使用 ForAttributeWithMetadataName 快速分析代码&lt;/h3&gt;

&lt;p&gt;从工程上进行分析发现，非常大量的分析生成任务都有一个特点，这个特点就是需要找到标记了某个 Attribute 特性的类型或方法或属性等，然后再做某个事情。这个特点其实源自于 dotnet C# 对于 Attribute 特性的设计。Attribute 特性是一种元数据，可以标记在类型、方法、属性等上面，用于描述这个类型、方法、属性等的特性。也常常用于标记给 IDE 和编译器看的，用于告诉 IDE 和编译器这个类型、方法、属性等的特性。比如常用的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ObsoleteAttribute&lt;/code&gt; 、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CallerMemberNameAttribute&lt;/code&gt; 、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DebuggerDisplayAttribute&lt;/code&gt; 等等&lt;/p&gt;

&lt;p&gt;在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IIncrementalGenerator&lt;/code&gt; 增量 Source Generator 源代码生成器中，提供了 ForAttributeWithMetadataName 工具方法。如此方法名所述，这个方法是用于找到标记了某个 Attribute 特性的类型、方法、属性等。这个方法的使用非常简单，只需要传递一个 Attribute 特性的完整名称，就可以找到标记了这个 Attribute 特性的类型、方法、属性等&lt;/p&gt;

&lt;p&gt;在上文的任务中，咱需要找到标记了某个 Attribute 特性的类型，然后将这些类型的名称输出到控制台。这个任务非常适合使用 ForAttributeWithMetadataName 方法来实现。接下来咱就来实现这个任务&lt;/p&gt;

&lt;p&gt;依然是新建两个项目，其中一个作为分析器项目，另一个作为被分析的项目。大家既可以在上文现有的项目中继续编写，也可以新建两个项目。这里我新建了一个名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NinahajawhuLairfoheahurcee.Analyzer&lt;/code&gt; 的分析器项目，和一个名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NinahajawhuLairfoheahurcee&lt;/code&gt; 的被分析项目。本文内容里面只给出关键代码片段，如需要全部的项目文件，可在下文找到所有代码的下载方法。如果自己编写的代码构建不通过或运行输出不符合预期，也推荐大家拉取本文的代码进行阅读&lt;/p&gt;

&lt;p&gt;先来完成任务需求分解中的第一部分，编写 FooAttribute 特性代码的生成。由于 FooAttribute 特性的代码不依赖任何分析结果，因此可以使用 RegisterPostInitializationOutput 方法生成。修改上文的 RegisterPostInitializationOutput 注册 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GeneratedCode.cs&lt;/code&gt; 的代码，将其替换为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FooAttribute.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;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Generator&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;IncrementalGenerator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IIncrementalGenerator&lt;/span&gt;
&lt;span class=&quot;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;IncrementalGeneratorInitializationContext&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;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterPostInitializationOutput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;initializationContext&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;initializationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;FooAttribute.cs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;
&lt;/span&gt;                &lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Lindexi&lt;/span&gt;&lt;span class=&quot;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;FooAttribute&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;span class=&quot;s&quot;&gt;&quot;&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;完成这一步之后，即可在业务端编写类型，将类型标记上 FooAttribute 特性。回到名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NinahajawhuLairfoheahurcee&lt;/code&gt; 的被分析项目，在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NinahajawhuLairfoheahurcee&lt;/code&gt; 控制台项目里面添加两个类型，让这两个类型标记上 FooAttribute 特性，用于后续测试类型被收集&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;Lindexi&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;NinahajawhuLairfoheahurcee&lt;/span&gt;&lt;span class=&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;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;F1&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&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;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;F2&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在 IIncrementalGenerator 增量 Source Generator 源代码生成，可在 IncrementalGeneratorInitializationContext 里面的 SyntaxProvider 属性，通过 ForAttributeWithMetadataName 快速收集标记了某个特性的类型、属性、方法等等&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;provider&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;SyntaxProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ForAttributeWithMetadataName&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;SyntaxNode&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;CancellationToken&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;p&quot;&gt;=&amp;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;n&quot;&gt;GeneratorAttributeSyntaxContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxContext&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;token&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;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;第一个参数是特性名，记得带上特性的命名空间，以及写明特性的全名。在正常的 C# 代码里面，都会忽略 Attribute 后缀，但是在这里需要带上 Attribute 后缀。第二个参数是语法判断条件，用于判断当前节点是否符合条件。第三个参数是语义处理和获取返回值，用于处理当前节点的语义，获取返回值&lt;/p&gt;

&lt;p&gt;那什么是语法，什么是语义呢？ 在 Roslyn 里面，将初步的代码分析的语法层面内容称为 Syntax 语法。语法是非常贴近编写出来的代码直接的内存映射的样子，这个过程里面只做片面考虑，即不考虑代码之间的引用关系，只考虑代码语法本身。语法分析过程是最早的过程，也是损耗极小的过程，也是可以并行化执行的过程。一般来说，进行语法分析都可以将写出来的代码分为一个个 SyntaxTree 语法树，每个代码或代码片都可以转换为一个 SyntaxNode 语法节点&lt;/p&gt;

&lt;p&gt;对应于 Syntax 语法的概念，语义 Semantic 则是包含了代码的含义，不仅仅只是语法层面上，语义 Semantic 包含了代码之间的引用关系，包含了各个符号的信息。语义分析过程是在语法分析之后的过程，执行过程中有所损耗，且存在多个代码文件和程序集之间的引用关联关系，这就是为什么在 IIncrementalGenerator 增量 Source Generator 源代码生成设计中是先做语法分析，判断结果通过，再做语义分析的原因&lt;/p&gt;

&lt;p&gt;再简单理解可以是如 C# 里面有分部类的概念，进行语法分析的时候，只能一次一个文件一个文件的分析，难以或无法直接分部类的其他分部在哪。但是进行语义分析的时候，可以将所有分部类的信息都收集起来，然后再进行分析，这样就能够找到所有分部类的信息。且在语义分析过程中，能够非常明确知道某个符号的确切含义&lt;/p&gt;

&lt;p&gt;语法和语义有比较庞大的知识，我将在后文的专门章节里面详细介绍。这里只是让大家粗略了解一下语法和语义的概念，以便大家能够更好理解后续的内容。本章内容也不会涉及多少的语法和语义知识，不需要对语法和语义有太多的了解，只需要知道这两个概念的存在即可&lt;/p&gt;

&lt;p&gt;粗略了解了一点语法和语义的概念，接下来咱就来实现 ForAttributeWithMetadataName 方法的使用。在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NinahajawhuLairfoheahurcee.Analyzer&lt;/code&gt; 分析器项目中，修改 IncrementalGenerator 类的 Initialize 方法，添加 ForAttributeWithMetadataName 方法的使用，如下所示&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;IncrementalValuesProvider&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;targetClassNameProvider&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;SyntaxProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ForAttributeWithMetadataName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Lindexi.FooAttribute&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;n&quot;&gt;SyntaxNode&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;CancellationToken&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;p&quot;&gt;=&amp;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;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;ClassDeclaration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratorAttributeSyntaxContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxContext&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;token&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;syntaxContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TargetSymbol&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;language-plaintext highlighter-rouge&quot;&gt;&quot;Lindexi.FooAttribute&quot;&lt;/code&gt; 字符串。此时将进入预设逻辑，增量的寻找所有标记了名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Lindexi.FooAttribute&quot;&lt;/code&gt; 特性的类型或属性或方法等等代码。一旦找到了标记了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Lindexi.FooAttribute&quot;&lt;/code&gt; 特性的代码，将会进入第二个参数的语法判断条件，即 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(SyntaxNode node, CancellationToken token) =&amp;gt; node.IsKind(SyntaxKind.ClassDeclaration)&lt;/code&gt; 代码块。此时将进入进一步判断，只有当找到的代码是类声明的时候，才是符合咱的任务需求的代码，即满足感兴趣的条件。这里的 SyntaxNode.IsKind 方法是判断当前传入的 SyntaxNode 是什么。前面步骤只是找到了标记了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Lindexi.FooAttribute&quot;&lt;/code&gt; 特性的代码，这里进一步判断找到的代码是不是类声明。满足前两个步骤，则证明这是一个在类型上面标记了名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Lindexi.FooAttribute&quot;&lt;/code&gt; 特性的代码，可以进入最后一个参数里面进行进一步的语义处理&lt;/p&gt;

&lt;p&gt;进一步的语义处理是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(GeneratorAttributeSyntaxContext syntaxContext, CancellationToken token) =&amp;gt; syntaxContext.TargetSymbol.Name&lt;/code&gt; 代码块。这里的 GeneratorAttributeSyntaxContext.TargetSymbol 属性是当前找到的代码的符号，即当前找到的代码的语义信息。这里的 TargetSymbol.Name 属性是当前找到的代码的名称，即当前找到的代码的类型名称。这里的代码块返回的是当前找到的代码的类型名称，即当前找到的代码的名称&lt;/p&gt;

&lt;p&gt;将其返回的内容是类似 Linq 的查询结果，即 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValuesProvider&amp;lt;string&amp;gt;&lt;/code&gt; 类型。这个类型是一个增量的值提供者，而不是立刻就返回一次所有满足条件的代码。在 Visual Studio 里面的执行逻辑上，大家可以认为是每更改、新增一次代码，就会执行一次这个查询逻辑，整个查询逻辑是源源不断执行的，不是一次性的，也不是瞬时全跑的，而是增量的逐步执行的&lt;/p&gt;

&lt;p&gt;执行过程也是一级级执行的，先通过了第一个参数的特性名，快速判断是否满足参数条件，再经过第二个参数进行语法判断。经过前面两个参数判断就可以快速过滤掉大量的代码，如此的方式可以极大减少计算工作量&lt;/p&gt;

&lt;p&gt;现在拿到了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValuesProvider&amp;lt;string&amp;gt;&lt;/code&gt; 返回值，能够从这里源源不断取出一个个类型出来。但按照咱的任务需求，咱是需要一口气收集所有类型的，不能一个个慢慢取。为此咱需要将 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValuesProvider&amp;lt;string&amp;gt;&lt;/code&gt; 给收集起来，成为一个集合数组。这里可以使用 Collect 方法进行收集，如下所示&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;IncrementalValueProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&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;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetClassNameArrayProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetClassNameProvider&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以看到此时返回值就从 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValuesProvider&amp;lt;string&amp;gt;&lt;/code&gt; 类型转换为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValueProvider&amp;lt;ImmutableArray&amp;lt;string&amp;gt;&amp;gt;&lt;/code&gt; 类型。核心不同在于 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;string&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImmutableArray&amp;lt;string&amp;gt;&lt;/code&gt; 不可变数组的差异而已。在整个 Roslyn 设计里面，大量采用不可变思想，这里的返回值就是不可变思想的一个体现。细心的伙伴可以看到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValuesProvider&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValueProvider&lt;/code&gt; 这两个单词的差别，没错，核心在于 Values 和 Value 的差别。在增量源代码生成器里面，使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValuesProvider&lt;/code&gt; 表示多值提供器，使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValueProvider&lt;/code&gt; 表示单值提供器，两者差异只是值提供器里面提供的数据是多项还是单项。使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Collect&lt;/code&gt; 方法可以将一个多值提供器的内容收集起来，收集为一个不可变集合，从而转换为一个单值提供器，这个单值提供器里面只有一项，且这一项是一个不可变数组。这部分细节内容将在下文和大家详细介绍，在本章节里面就不过多描述&lt;/p&gt;

&lt;p&gt;最后一步就是将 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValueProvider&amp;lt;ImmutableArray&amp;lt;string&amp;gt;&amp;gt;&lt;/code&gt; 返回值注册到输出源代码中。在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalGenerator&lt;/code&gt; 类的 Initialize 方法里面，使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;context.RegisterSourceOutput&lt;/code&gt; 方法注册输出源代码，如下所示&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterSourceOutput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;targetClassNameArrayProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;productionContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;classNameArray&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;productionContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;GeneratedCode.cs&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;$&quot;&quot;&quot;
&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;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;NinahajawhuLairfoheahurcee&lt;/span&gt;
                &lt;span class=&quot;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;GeneratedCode&lt;/span&gt;
                    &lt;span class=&quot;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;{&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 特性的类型有： ｛｛string.Join(&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;, classNameArray)｝｝&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;s&quot;&gt;&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;/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;标记了 Foo 特性的类型有： F1,F2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;尝试展开 Visual Studio 的 依赖项-&amp;gt;分析器，如下图所示&lt;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门0.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20252142036427475.jpg&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;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;NinahajawhuLairfoheahurcee&lt;/span&gt;
&lt;span class=&quot;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;GeneratedCode&lt;/span&gt;
    &lt;span class=&quot;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;{&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 特性的类型有： F2,F1&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;如此以来，对比传统的反射的方法，源代码生成的方式可以将耗时完全放在开发编译过程，不会占用用户端的执行时间。且这个过程都是完完全全的直接代码，也方便运行时的 JIT 进行优化，大大提升了运行时间。完完全全的直接代码也带来了静态分析的友好，可以作为代码裁剪和 AOT 的底层支持&lt;/p&gt;

&lt;p&gt;喜欢点点的伙伴也许在准备写 RegisterSourceOutput 的时候，就发现了还有一个名为 RegisterImplementationSourceOutput 方法，那 RegisterSourceOutput 和 RegisterImplementationSourceOutput 的差别是什么？这两个方法对最终生成的代码是没有影响的，核心差别是 RegisterImplementationSourceOutput 是用来注册具体实现生成的代码，这部分输入的代码会被 IDE 作为可选分析项。如 RegisterImplementationSourceOutput 命名所述，这是一个用来注册“具体实现”的代码，在代码里面，咱可以强行将代码分为“定义代码”和“实现代码”，比如说方法签名是定义代码，方法体是实现代码。从 IDE 的分析角度来看，只对“定义代码”而跳过“实现代码”，可以更大程度的减少分析压力，提升分析速度。通过 RegisterImplementationSourceOutput 方法注册的代码，会被 IDE 作为可选分析项，不会因为生成了大量代码导致 IDE 过于卡顿。但带来的问题是这部分生成代码可能不被加入 IDE 分析，导致业务方调用时飘红。因此通过 RegisterImplementationSourceOutput 生成的代码，基本要求是不会被业务方直接调用。常用的套路是先通过 RegisterSourceOutput 或甚至是 RegisterPostInitializationOutput 生成分部类或分部方法，然后再慢慢在 RegisterImplementationSourceOutput 里面填充实现代码。如果感觉对 RegisterSourceOutput 和 RegisterImplementationSourceOutput 的差别还是很混乱，没关系，咱将在后文通过实践来让大家更好地理解两者的差别&lt;/p&gt;

&lt;p&gt;以上就是通过 ForAttributeWithMetadataName 开始入门编写分析和收集和生成的简单例子，如果对 ForAttributeWithMetadataName 使用方法感兴趣，扩展阅读部分请参阅 &lt;a href=&quot;/post/%E4%BD%BF%E7%94%A8-ForAttributeWithMetadataName-%E6%8F%90%E9%AB%98-IIncrementalGenerator-%E5%A2%9E%E9%87%8F-Source-Generator-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90%E5%BC%80%E5%8F%91%E6%95%88%E7%8E%87%E5%92%8C%E6%80%A7%E8%83%BD.html&quot;&gt;使用 ForAttributeWithMetadataName 提高 IIncrementalGenerator 增量 Source Generator 源代码生成开发效率和性能&lt;/a&gt; 
&lt;!-- [使用 ForAttributeWithMetadataName 提高 IIncrementalGenerator 增量 Source Generator 源代码生成开发效率和性能 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18009107 ) --&gt;&lt;/p&gt;

&lt;p&gt;如果大家照着以上的例子编写不出来能构建通过的代码，或者是运行代码不符合预期，欢迎拉取我的示例代码进行阅读&lt;/p&gt;

&lt;p&gt;同样的，以上代码放在 &lt;a href=&quot;https://github.com/lindexi/lindexi_gd/tree/b8c036de9d9d7c4b1a3d329054086d6566d14dc4/Roslyn/NinahajawhuLairfoheahurcee&quot;&gt;github&lt;/a&gt; 和 &lt;a href=&quot;https://gitee.com/lindexi/lindexi_gd/tree/b8c036de9d9d7c4b1a3d329054086d6566d14dc4/Roslyn/NinahajawhuLairfoheahurcee&quot;&gt;gitee&lt;/a&gt; 上，可以使用如下命令行拉取代码。我整个代码仓库比较庞大，使用以下命令行可以进行部分拉取，拉取速度比较快&lt;/p&gt;

&lt;p&gt;先创建一个空文件夹，接着使用命令行 cd 命令进入此空文件夹，在命令行里面输入以下代码，即可获取到本文的代码&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;git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin b8c036de9d9d7c4b1a3d329054086d6566d14dc4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上使用的是国内的 gitee 的源，如果 gitee 不能访问，请替换为 github 的源。请在命令行继续输入以下代码，将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码，可以发邮件向我要代码&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;git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin b8c036de9d9d7c4b1a3d329054086d6566d14dc4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;获取代码之后，进入 Roslyn/NinahajawhuLairfoheahurcee 文件夹，即可获取到源代码&lt;/p&gt;

&lt;h2 id=&quot;更底层的收集分析和生成&quot;&gt;更底层的收集分析和生成&lt;/h2&gt;

&lt;p&gt;阅读到这里，也许大家会感慨，使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ForAttributeWithMetadataName&lt;/code&gt; 还是有很大的限制。比如我的需求任务是分析任意的继承了 IFoo 接口的代码，而没有任何的标记，那应该如何做呢？只通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ForAttributeWithMetadataName&lt;/code&gt; 是无法实现的。这个时候就需要更底层的收集分析和生成技术&lt;/p&gt;

&lt;p&gt;本文会和大家介绍 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ForAttributeWithMetadataName&lt;/code&gt; 仅仅只是因为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ForAttributeWithMetadataName&lt;/code&gt; 方法使用简单，且使用频率高。不代表只能通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ForAttributeWithMetadataName&lt;/code&gt; 方法进行分析和生成。实际上，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ForAttributeWithMetadataName&lt;/code&gt; 方法只是对更底层的收集分析和生成技术的封装，更底层的收集分析和生成技术是可以实现更多的需求任务的&lt;/p&gt;

&lt;p&gt;在 IIncrementalGenerator 增量 Source Generator 源代码生成里面提供了众多数据源入口，比如整个的配置、引用的程序集、源代码等等。最核心也是用最多的就是通过提供的源代码数据源进行收集分析&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;/ul&gt;
  &lt;/li&gt;
  &lt;li&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;ul&gt;
      &lt;li&gt;这一步的逻辑和普通的 Source Generator 是相同的，只是输入的参数不同&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;按照以上的步骤，咱来开始重新实现上文的使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ForAttributeWithMetadataName&lt;/code&gt; 实现的任务需求。这次咱将不使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ForAttributeWithMetadataName&lt;/code&gt; 方法，而是使用更底层的收集分析和生成技术。在这个实现过程中，大家也能感受到使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ForAttributeWithMetadataName&lt;/code&gt; 方法的便捷性&lt;/p&gt;

&lt;p&gt;为了方便大家后续拉取代码方便，防止多个版本之间的代码误导。我这里重新新建了两个项目，分别是名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BegalllalhereCilaywhonerdem.Analyzer&lt;/code&gt; 的分析器项目，和一个名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BegalllalhereCilaywhonerdem&lt;/code&gt; 的被分析项目。本文内容里面只给出关键代码片段，如需要全部的项目文件，可在下文找到所有代码的下载方法。如果自己编写的代码构建不通过或运行输出不符合预期，也推荐大家拉取本文的代码进行阅读&lt;/p&gt;

&lt;p&gt;先完全按照上文的方式进行项目组织，甚至是完全的代码拷贝。因为接下来咱简要替换的部分只是将原本的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ForAttributeWithMetadataName&lt;/code&gt; 相关代码进行替换而已，其他逻辑依然保持不变&lt;/p&gt;

&lt;p&gt;删掉原本的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ForAttributeWithMetadataName&lt;/code&gt; 相关代码，即删掉如下代码&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;IncrementalValuesProvider&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;targetClassNameProvider&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;SyntaxProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ForAttributeWithMetadataName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Lindexi.FooAttribute&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;n&quot;&gt;SyntaxNode&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;CancellationToken&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;p&quot;&gt;=&amp;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;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;ClassDeclaration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratorAttributeSyntaxContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxContext&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;token&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;syntaxContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TargetSymbol&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;IncrementalValueProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&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;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetClassNameArrayProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetClassNameProvider&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;接下来咱将使用更底层的收集分析和生成技术，即从 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;context.SyntaxProvider.CreateSyntaxProvider&lt;/code&gt; 方法开始&lt;/p&gt;

&lt;p&gt;在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;context.SyntaxProvider.CreateSyntaxProvider&lt;/code&gt; 方法里面包含两个参数，第一个参数是一个进行语法判断的过程，第二个参数是进行语义进一步判断和加工处理的逻辑。也就是说 CreateSyntaxProvider 方法就包含了上文所述的“告诉框架层需要关注哪些文件或内容或配置的变更”和“告诉框架层从变更的文件里面感兴趣什么数据，对数据预先进行处理”两个步骤&lt;/p&gt;

&lt;p&gt;在 CreateSyntaxProvider 方法里面，第一步的语法判断是判断当前传入的是否类型定义。如果是类型定义，则读取其标记的特性，判断特性满足 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Lindexi.FooAttribute&lt;/code&gt; 的特征时，则算语法判断通过，让数据走到下面的语义判断处理上。其代码大概如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;IncrementalValueProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&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;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetClassNameArrayProvider&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;SyntaxProvider&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateSyntaxProvider&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;_&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;node&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;ClassDeclarationSyntax&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;classDeclarationSyntax&lt;/span&gt;&lt;span class=&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;// 为什么这里是 Attribute List 的集合？原因是可以写出这样的语法&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// ```csharp&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// [A1Attribute, A2Attribute]&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// [A3Attribute]&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// private void Foo()&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;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AttributeListSyntax&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attributeListSyntax&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;classDeclarationSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AttributeLists&lt;/span&gt;&lt;span class=&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;AttributeSyntax&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attributeSyntax&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attributeListSyntax&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;n&quot;&gt;NameSyntax&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;attributeSyntax&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;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nameText&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;ToFullString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nameText&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Foo&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;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;nameText&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;FooAttribute&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;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;// 可能还有 global::Lindexi.FooAttribute 的情况&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nameText&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;Lindexi.FooAttribute&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;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;nameText&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;Lindexi.Foo&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;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;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;n&quot;&gt;syntaxContext&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;)&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;Collect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如上述的代码所示，首先是经过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if (node is not ClassDeclarationSyntax classDeclarationSyntax)&lt;/code&gt; 判断，过滤掉非类型定义部分的代码。此时就可以确保大量的代码都不会进入到后续分支。毕竟对于正常的代码逻辑来说，类型的定义还是少数哈。接着的逻辑编写就有些考大家对于 C# 的基础语法知识了，先获取特性列表。这里获取到的是列表的集合，为什么呢？因为在 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;A1Attribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;A2Attribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;A3Attribute&lt;/span&gt;&lt;span class=&quot;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;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;language-plaintext highlighter-rouge&quot;&gt;[A1Attribute, A2Attribute]&lt;/code&gt; 就是一个特性列表，而 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[A1Attribute, A2Attribute]&lt;/code&gt; 和  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A3Attribute&lt;/code&gt; 三个特性构成了特性列表的集合，如此才能保证能够获取到所有的特性且不丢失语法上的特征。即可能某些特性是和其他的特性写在一起的特征才不会被丢失。这就是为什么需要有两层的 foreach 循环才能遍历所有的特性的原因&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;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;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;F1&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在语法层面上只能知道 F1 类型标记了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[Foo]&lt;/code&gt; 特性，但不知道这个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[Foo]&lt;/code&gt; 特性是否真的是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Lindexi.FooAttribute&lt;/code&gt; 特性。需要在语义分析过程中，进一步判断是否真的是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Lindexi.FooAttribute&lt;/code&gt; 特性。语法层面上只能知道写下去的是什么代码，完全字面量。这也就是为什么上面代码的判断逻辑会额外多了那么多判断的原因。当然了，如果大家图省事，那直接判断是否包含 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 字符串也可以的&lt;/p&gt;

&lt;p&gt;上面代码使用了对 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NameSyntax&lt;/code&gt; 调用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ToFullString&lt;/code&gt; 方法获取到所标记的名，再通过字符串判断逻辑，判断是否可能是标记了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Lindexi.FooAttribute&lt;/code&gt; 特性&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;NameSyntax&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;attributeSyntax&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;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nameText&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;ToFullString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

                        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nameText&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Foo&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;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;nameText&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;FooAttribute&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;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;// 可能还有 global::Lindexi.FooAttribute 的情况&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nameText&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;Lindexi.FooAttribute&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;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;nameText&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;Lindexi.Foo&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;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;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NameSyntax&lt;/code&gt; 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ToFullString&lt;/code&gt; 感兴趣，请参阅 &lt;a href=&quot;/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;/p&gt;

&lt;p&gt;虽然上文判断逻辑看起来写的很多，但也不代表能通过语法判断逻辑的，就一定是标记了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Lindexi.FooAttribute&lt;/code&gt; 特性。在语义部分进行进一步处理，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxProvider&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateSyntaxProvider&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;_&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;n&quot;&gt;syntaxContext&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;)&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;ISymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;declaredSymbol&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SemanticModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetDeclaredSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;syntaxContext&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;declaredSymbol&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;INamedTypeSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;namedTypeSymbol&lt;/span&gt;&lt;span class=&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;kt&quot;&gt;string&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;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AttributeData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attributeDataArray&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;namedTypeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAttributes&lt;/span&gt;&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;attributeDataArray&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;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;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AttributeClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToDisplayString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SymbolDisplayFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullyQualifiedFormat&lt;/span&gt;&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;global::Lindexi.FooAttribute&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;kt&quot;&gt;string&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;namedTypeSymbol&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;由于在语法分析过程中，只能知道标记了名为 Foo 的特性，不知道是否真的是特性。需要在语义分析过程中，进一步判断是否真的是特性。进一步判断的方法就是通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetAttributes&lt;/code&gt; 方法获取标记在类型上面的特性，此时和语法不同的是，可以拿到分部类上面标记的特性，不单单只是某个类型文件而已。接着使用 ToDisplayString 方法获取标记的特性的全名，判断全名是否为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;global::Lindexi.FooAttribute&lt;/code&gt; 从而确保类型符合预期。当然了，这个过程里面，咱是省略了判断 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;global::Lindexi.FooAttribute&lt;/code&gt; 特性是属于哪个程序集的。正常的分析器项目里面也不会真的去判断某个全名的类型属于哪个程序集的。这个方法即是缺陷也是功能，方便很多开发者只要写出来“鸭子”类型即可的行为。这里说的“鸭子”行为就是只要一个类型的命名空间和名字符合约定即可，至于这个类型是放在哪个程序集和用什么方式的可访问描述都不重要。许多的 C# 高版本语法也是这么定义出来的，如 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;init&lt;/code&gt; 或 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ValueTuple&lt;/code&gt; 等等。因为通过这样的设计，可以更好的让 C# 语言和具体的框架分离，这也是 C# dotnet 的设计基本原则&lt;/p&gt;

&lt;p&gt;在通过了语义判断逻辑之后，即可决定返回值是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(string) null&lt;/code&gt; 还是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;namedTypeSymbol.Name&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;IncrementalValueProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&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;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetClassNameArrayProvider&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;SyntaxProvider&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateSyntaxProvider&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;_&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;node&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;ClassDeclarationSyntax&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;classDeclarationSyntax&lt;/span&gt;&lt;span class=&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;// 为什么这里是 Attribute List 的集合？原因是可以写出这样的语法&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// ```csharp&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// [A1Attribute, A2Attribute]&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// [A3Attribute]&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// private void Foo()&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;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AttributeListSyntax&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attributeListSyntax&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;classDeclarationSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AttributeLists&lt;/span&gt;&lt;span class=&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;AttributeSyntax&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attributeSyntax&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attributeListSyntax&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;n&quot;&gt;NameSyntax&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;attributeSyntax&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;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nameText&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;ToFullString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nameText&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Foo&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;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;nameText&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;FooAttribute&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;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;// 可能还有 global::Lindexi.FooAttribute 的情况&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nameText&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;Lindexi.FooAttribute&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;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;nameText&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;Lindexi.Foo&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;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;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;n&quot;&gt;syntaxContext&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;)&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;ISymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;declaredSymbol&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SemanticModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetDeclaredSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;syntaxContext&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;declaredSymbol&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;INamedTypeSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;namedTypeSymbol&lt;/span&gt;&lt;span class=&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;kt&quot;&gt;string&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;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AttributeData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attributeDataArray&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;namedTypeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAttributes&lt;/span&gt;&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;attributeDataArray&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;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;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AttributeClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToDisplayString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SymbolDisplayFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullyQualifiedFormat&lt;/span&gt;&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;global::Lindexi.FooAttribute&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;kt&quot;&gt;string&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;namedTypeSymbol&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;nf&quot;&gt;Collect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;依然和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;使用 ForAttributeWithMetadataName 快速分析代码&lt;/code&gt; 章一样，将 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;targetClassNameArrayProvider&lt;/code&gt; 注册到输出源代码中，如下所示&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterSourceOutput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;targetClassNameArrayProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;productionContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;classNameArray&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;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;这就是直接使用 CreateSyntaxProvider 方法进行语法语义分析代替 ForAttributeWithMetadataName 的方式&lt;/p&gt;

&lt;p&gt;以上代码放在 &lt;a href=&quot;https://github.com/lindexi/lindexi_gd/tree/cde8c2a0bd1da7a17467655ff1fc1d78ad28fbed/Roslyn/BegalllalhereCilaywhonerdem&quot;&gt;github&lt;/a&gt; 和 &lt;a href=&quot;https://gitee.com/lindexi/lindexi_gd/tree/cde8c2a0bd1da7a17467655ff1fc1d78ad28fbed/Roslyn/BegalllalhereCilaywhonerdem&quot;&gt;gitee&lt;/a&gt; 上，可以使用如下命令行拉取代码。我整个代码仓库比较庞大，使用以下命令行可以进行部分拉取，拉取速度比较快&lt;/p&gt;

&lt;p&gt;先创建一个空文件夹，接着使用命令行 cd 命令进入此空文件夹，在命令行里面输入以下代码，即可获取到本文的代码&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;git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin cde8c2a0bd1da7a17467655ff1fc1d78ad28fbed
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上使用的是国内的 gitee 的源，如果 gitee 不能访问，请替换为 github 的源。请在命令行继续输入以下代码，将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码，可以发邮件向我要代码&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;git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin cde8c2a0bd1da7a17467655ff1fc1d78ad28fbed
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;获取代码之后，进入 Roslyn/BegalllalhereCilaywhonerdem 文件夹，即可获取到源代码&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;language-plaintext highlighter-rouge&quot;&gt;ChunecilarkenaLibeewhemke&lt;/code&gt; 的分析器项目，和名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChunecilarkenaLibeewhemke.Test&lt;/code&gt; 的单元测试项目。其中名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChunecilarkenaLibeewhemke&lt;/code&gt; 的分析器项目里面的内容和上一章提供的代码相同，在本章里面咱重点将放在单元测试项目上&lt;/p&gt;

&lt;p&gt;先设置让 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChunecilarkenaLibeewhemke.Test&lt;/code&gt; 单元测试项目引用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChunecilarkenaLibeewhemke&lt;/code&gt; 分析器项目。和前文提及的引用分析器项目不同的是，在单元测试里面就应该添加程序集应用，如此才能够让单元测试项目访问到分析器项目的公开成员，从而进行测试。以下是我设置了单元测试引用分析器项目之后的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChunecilarkenaLibeewhemke.Test&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;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;..\ChunecilarkenaLibeewhemke\ChunecilarkenaLibeewhemke.csproj&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ReferenceOutputAssembly=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;OutputItemType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Analyzer&quot;&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;language-plaintext highlighter-rouge&quot;&gt;OutputItemType=&quot;Analyzer&quot;&lt;/code&gt; 是可选的，仅仅用在期望额外将单元测试项目也当成被分析项目时才添加。默认 ReferenceOutputAssembly 属性值就是 true 值，这里强行写 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReferenceOutputAssembly=&quot;true&quot;&lt;/code&gt; 只是为了强调而已，默认不写即可。即默认情况下，只需使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;ProjectReference Include=&quot;..\ChunecilarkenaLibeewhemke\ChunecilarkenaLibeewhemke.csproj&quot; /&amp;gt;&lt;/code&gt; 代码引用即可，和其他单元测试项目没有什么差别&lt;/p&gt;

&lt;p&gt;单元测试项目需要添加单元测试负载，这里需要额外添加针对分析器的负载。添加之后的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChunecilarkenaLibeewhemke.Test&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;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;net8.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;ImplicitUsings&amp;gt;&lt;/span&gt;enable&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ImplicitUsings&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Nullable&amp;gt;&lt;/span&gt;enable&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Nullable&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.NET.Test.Sdk&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;17.13.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.TestAdapter&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3.8.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;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;3.8.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;Microsoft.CodeAnalysis.Analyzers&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3.11.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;Microsoft.CodeAnalysis.CSharp&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.12.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;Microsoft.CodeAnalysis.CSharp.Workspaces&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.12.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;Microsoft.CodeAnalysis.Common&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.12.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;Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.MSTest&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.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;Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.MSTest&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.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;Microsoft.CodeAnalysis.CSharp.CodeRefactoring.Testing.MSTest&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.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;Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.MSTest&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.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;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;..\ChunecilarkenaLibeewhemke\ChunecilarkenaLibeewhemke.csproj&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ReferenceOutputAssembly=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;OutputItemType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Analyzer&quot;&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;单元测试项目可以是尽可能的高版本的 .NET 版本，只有分析器项目才在当前为了兼容 VisualStudio 才需要选用旧的 netstandard2.0 版本。单元测试项目是可以独立执行的，也不会被其他模块引用，尽可能高版本可以享用更新的技术&lt;/p&gt;

&lt;p&gt;完成单元测试项目的基础准备之后，接下来咱开始新建名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalGeneratorTest&lt;/code&gt; 的单元测试类。在对分析器，特别是源代码生成器的单元测试中，一般都会通过一个自己编写的 CreateCompilation 方法，这个方法的作用是将传入的源代码字符串封装为 CSharpCompilation 类型。接着使用 CSharpGeneratorDriver 执行指定的源代码生成器&lt;/p&gt;

&lt;p&gt;常用的封装 CSharpCompilation 代码的 CreateCompilation 方法代码如下。可以简单将 CSharpCompilation 理解为一个虚拟的项目。一个虚拟的项目重要的部分只有两个，一个就是源代码本身，另一个就是所引用的程序集。在单元测试的源代码本身就是通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CSharpSyntaxTree.ParseText&lt;/code&gt; 方法将源代码转换为 SyntaxTree 对象。引用程序集可能会复杂一些，在咱这个单元测试里面只需要带上 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System.Runtime&lt;/code&gt; 程序集即可，带上的方法是通过某个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System.Runtime&lt;/code&gt; 程序集的类型，如 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System.Reflection.Binder&lt;/code&gt; 类型，取其类型所在程序集的路径，再通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MetadataReference.CreateFromFile&lt;/code&gt; 作为引用路径&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;CSharpCompilation&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateCompilation&lt;/span&gt;&lt;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;p&quot;&gt;=&amp;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;compilation&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;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;source&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;Foo.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;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;c1&quot;&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;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Binder&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;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;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;ConsoleApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;大部分情况下的分析器单元测试项目的 CSharpCompilation 封装代码相对固定，会变更的只有某些引用逻辑而已&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;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;IncrementalGeneratorTest&lt;/span&gt;
&lt;span class=&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;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;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;language-plaintext highlighter-rouge&quot;&gt;testCode&lt;/code&gt; 变量里面，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;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;testCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;
&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;Lindexi&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;ChunecilarkenaLibeewhemke.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;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;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;F1&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            
                &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FooAttribute&lt;/span&gt;&lt;span class=&quot;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;F2&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &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;&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;先调用刚才的 CreateCompilation 方法，将 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testCode&lt;/code&gt; 封装为 CSharpCompilation 对象。再创建出期望测试的源代码生成器类型。在一个分析器里面里面可以包含非常多个源代码生成器，在单元测试里面可以非常方便取出期望进行测试的源代码生成器，进行非常特定的测试。这也是单元测试能够带来的多入口的优势。本文这里将测试自己项目里面的名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalGenerator&lt;/code&gt; 的源代码生成器&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;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;IncrementalGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;调用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CSharpGeneratorDriver.Create&lt;/code&gt; 创建出 GeneratorDriver 对象，用于在单元测试里面执行源代码生成器，从而获取其执行结果。这里需要说明的是整个 Roslyn 都在贯穿不可变设计。不例外，这个 GeneratorDriver 类型也是不可变对象，即在执行源代码生成器之后，是返回一个新的 GeneratorDriver 对象，原本的对象的状态是不改变的。这个设计上可能会让一些伙伴踩坑，让伙伴们发现在执行源代码生成器之后，调用 GeneratorDriver 的 GetRunResult 方法拿不到结果，这是因为调用的 GeneratorDriver 对象还是旧的对象，而是不执行源代码生成器之后的新的对象&lt;/p&gt;

&lt;p&gt;为什么 Roslyn 要这么设计 GeneratorDriver 类型呢？除了不可变能够带来很大程度上的降低程序复杂度，方便出现问题快速重现问题和获取过程状态之外。另一个重要原因是可以让 IDE 从某个状态重复多次快速进入下一个状态，而不需要每次都创建新的对象。如咱在某个方法里面开始编写代码，从进入方法开始的状态就可以保留，不断编写代码，不断输入字符或单词的过程中，就可以不断后台调用 GeneratorDriver 对象进行执行源代码生成状态，而不需要每输入一次都创建一次新的对象。如此可以更好的提升 IDE 的性能&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;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;IncrementalGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;GeneratorDriver&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CSharpGeneratorDriver&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;generator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;调用 CSharpGeneratorDriver 的 RunGenerators 执行源代码生成器，记得获取其方法返回值作为新的对象，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;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;IncrementalGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;GeneratorDriver&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CSharpGeneratorDriver&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;generator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;GeneratorDriver&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;driver2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RunGenerators&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;尝试获取 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;driver2&lt;/code&gt; 的结果，获取到源代码生成器输出的源代码内容，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;generatedTree&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;driver2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetRunResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratedTrees&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generatedCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generatedTree&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;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;generatedCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span 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/%E4%B8%BA-IIncrementalGenerator-%E5%A2%9E%E9%87%8F-Source-Generator-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90%E9%A1%B9%E7%9B%AE%E6%B7%BB%E5%8A%A0%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95.html&quot;&gt;为 IIncrementalGenerator 增量 Source Generator 源代码生成项目添加单元测试&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;在完成单元测试的搭建之后，自然咱可以添加更多测试逻辑。比如说上文提及的在语法层面上只能知道一个类型标记了名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 的特性，而不知此 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 具体的是什么样的类型。需要通过进一步的语义过程的判断处理。在这里，咱将通过单元测试构建出这样的情况，进行测试咱的源代码生成器逻辑&lt;/p&gt;

&lt;p&gt;编辑放在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testCode&lt;/code&gt; 的代码，给其添加一些捣乱的代码，更改之后的代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;testCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;
&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;Lindexi&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;ChunecilarkenaLibeewhemke.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;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;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;F1&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            
                &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FooAttribute&lt;/span&gt;&lt;span class=&quot;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;F2&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&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;FooChunecilarkenaLibeewhemke&lt;/span&gt;
            &lt;span class=&quot;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;FooAttribute&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;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;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;F3&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &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;&quot;;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如上面代码所示，添加了一个放在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FooChunecilarkenaLibeewhemke&lt;/code&gt; 命名空间下的用于捣乱的 F3 类型，这个 F3 类型实际上标记的是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FooChunecilarkenaLibeewhemke.FooAttribute&lt;/code&gt; 特性，而不是源代码生成器期望的标记了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Lindexi.FooAttribute&lt;/code&gt; 特性&lt;/p&gt;

&lt;p&gt;尝试调试此单元测试代码，在语义判断处打上断点&lt;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门1.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2025224851237916.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;此时可见在语义判断层面上进入了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if (!attributeDataArray.Any(t =&amp;gt; t.AttributeClass?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) == &quot;global::Lindexi.FooAttribute&quot;))&lt;/code&gt; 判断分支，这就意味着前面的语法判断过程中是放过了 F3 类型这个情况，只有在语义过程中，获取其全名才拿到了真实的名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;global::FooChunecilarkenaLibeewhemke.FooAttribute&lt;/code&gt; 的全名，从而将其过滤掉。符合预期的就是只输出 F1 和 F2 类型，过滤掉 F3 类型。咱可以在单元测试里面，为生成的代码添加固定测试，确保在变更逻辑的时候，如果有生成代码逻辑变动可以进行拦截。如以下代码所示&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;GeneratorDriver&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CSharpGeneratorDriver&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;generator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RunGenerators&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;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;generatedTree&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetRunResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratedTrees&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generatedCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generatedTree&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;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;generatedCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;generatedTree&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;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;GeneratedCode.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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;
&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;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ChunecilarkenaLibeewhemke&lt;/span&gt;
                     &lt;span class=&quot;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;GeneratedCode&lt;/span&gt;
                         &lt;span class=&quot;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;{&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 特性的类型有： F1,F2,&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;s&quot;&gt;&quot;&quot;&quot;;
&lt;/span&gt;                &lt;span class=&quot;c1&quot;&gt;// 防止拉取 git 时出现的 \r\n 不匹配问题。能够解决一些拉取 git 的奇怪的坑，也就是在我电脑上跑的好好的，但为什么在你电脑上就炸了&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expected&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;\r\n&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;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;expected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&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;\r\n&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;p&quot;&gt;}&lt;/span&gt;
        &lt;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;p&gt;本章的代码放在 &lt;a href=&quot;https://github.com/lindexi/lindexi_gd/tree/abe3f751fe987a29d0b241501fade1d20c2dc74a/Roslyn/ChunecilarkenaLibeewhemke&quot;&gt;github&lt;/a&gt; 和 &lt;a href=&quot;https://gitee.com/lindexi/lindexi_gd/tree/abe3f751fe987a29d0b241501fade1d20c2dc74a/Roslyn/ChunecilarkenaLibeewhemke&quot;&gt;gitee&lt;/a&gt; 上，可以使用如下命令行拉取代码。我整个代码仓库比较庞大，使用以下命令行可以进行部分拉取，拉取速度比较快&lt;/p&gt;

&lt;p&gt;先创建一个空文件夹，接着使用命令行 cd 命令进入此空文件夹，在命令行里面输入以下代码，即可获取到本文的代码&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;git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin abe3f751fe987a29d0b241501fade1d20c2dc74a
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上使用的是国内的 gitee 的源，如果 gitee 不能访问，请替换为 github 的源。请在命令行继续输入以下代码，将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码，可以发邮件向我要代码&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;git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin abe3f751fe987a29d0b241501fade1d20c2dc74a
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;获取代码之后，进入 Roslyn/ChunecilarkenaLibeewhemke 文件夹，即可获取到源代码&lt;/p&gt;

&lt;p&gt;如果在单元测试中，需要测试到 AnalyzerConfigOptionsProvider 选项，请参阅 &lt;a href=&quot;/post/IIncrementalGenerator-%E5%A6%82%E4%BD%95%E5%9C%A8%E6%BA%90%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90%E5%99%A8%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95%E6%8F%90%E4%BE%9B-AnalyzerConfigOptionsProvider-%E9%80%89%E9%A1%B9.html&quot;&gt;IIncrementalGenerator 如何在源代码生成器单元测试提供 AnalyzerConfigOptionsProvider 选项&lt;/a&gt;
&lt;!-- [IIncrementalGenerator 如何在源代码生成器单元测试提供 AnalyzerConfigOptionsProvider 选项 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/19275194 ) --&gt;&lt;/p&gt;

&lt;h2 id=&quot;直接调试项目&quot;&gt;直接调试项目&lt;/h2&gt;

&lt;p&gt;在上一章中，和大家介绍了如何编写单元测试。在此过程中，也许有些伙伴会感觉编写单元测试还是比较繁琐的。或者说在编写单元测试的过程里面会比较耗时，纯字符串方式也没有代码提示，不太适合很多伙伴的工作现状。在大型项目中，或比较正式的项目里面，添加单元测试来提升分析器的稳定性，以及通过更多单元测试测试更多分支。而在许多没有那么多资源可以投入的情况下，则可以追求简单的直接调试项目&lt;/p&gt;

&lt;p&gt;简单的直接调试项目的方式指的是直接从分析器项目上，在 VisualStudio 里面一键 F5 就可以启动调试，调试入口和其他任何 dotnet 项目相同，非常方便。不需要去新建一个单元测试项目，可以直接对着目标项目，即被分析项目，进行调试。可以减少在单元测试里面搭建项目引用关系，搭建项目组织等的工作量&lt;/p&gt;

&lt;p&gt;直接调试要求 Visual Studio 安装好了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.NET Compiler Platform SDK&lt;/code&gt; 负载组件，这个组件是用于支持 Roslyn 的调试环境。给 Visual Studio 打上 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.NET Compiler Platform SDK&lt;/code&gt; 负载组件方法如下：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;运行“Visual Studio 安装程序”&lt;/li&gt;
  &lt;li&gt;选择“修改”&lt;/li&gt;
  &lt;li&gt;检查“Visual Studio 扩展开发”工作负荷。&lt;/li&gt;
  &lt;li&gt;在摘要树中打开“Visual Studio 扩展开发”节点。&lt;/li&gt;
  &lt;li&gt;选中“.NET Compiler Platform SDK”框。 将在可选组件最下面找到它&lt;/li&gt;
&lt;/ol&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门10.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202533856536565.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门11.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20253385843486.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;依然是为了让大家方便获取正确的代码起见，我这里继续新建两个项目，分别是名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JehairqogefaKaiwuwhailallkihaiki.Analyzer&lt;/code&gt; 的分析器项目和名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JehairqogefaKaiwuwhailallkihaiki&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;language-plaintext highlighter-rouge&quot;&gt;IsRoslynComponent&lt;/code&gt; 属性。即在分析器项目的 csproj 项目文件的 PropertyGroup 里面存在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;IsRoslynComponent&amp;gt;true&amp;lt;/IsRoslynComponent&amp;gt;&lt;/code&gt; 代码片段。这个属性是告诉 VisualStudio 这是一个 Roslyn 组件，从而可以在调试的时候启动 Roslyn 的调试环境&lt;/li&gt;
  &lt;li&gt;确保被调试项目正确添加了分析器项目引用，配置了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OutputItemType=&quot;Analyzer&quot;&lt;/code&gt; 方式的引用&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;以下为分析器项目和被分析的控制台项目的 csproj 项目文件内容，大家可以对比一下自己的项目是否符合要求&lt;/p&gt;

&lt;p&gt;分析器项目：&lt;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门2.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2025227854402566.jpg&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;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;LangVersion&amp;gt;&lt;/span&gt;latest&lt;span class=&quot;nt&quot;&gt;&amp;lt;/LangVersion&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;EnforceExtendedAnalyzerRules&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/EnforceExtendedAnalyzerRules&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;IsRoslynComponent&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IsRoslynComponent&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.CSharp&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.11.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;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门3.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2025227855225980.jpg&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;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net9.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;ImplicitUsings&amp;gt;&lt;/span&gt;enable&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ImplicitUsings&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Nullable&amp;gt;&lt;/span&gt;enable&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Nullable&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;ProjectReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..\JehairqogefaKaiwuwhailallkihaiki.Analyzer\JehairqogefaKaiwuwhailallkihaiki.Analyzer.csproj&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;OutputItemType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Analyzer&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;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;h3 id=&quot;手工配置&quot;&gt;手工配置&lt;/h3&gt;

&lt;p&gt;在分析器项目上新建 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Properties\launchSettings.json&lt;/code&gt; 调试启动配置文件。即在 Properties 文件夹里新建名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launchSettings.json&lt;/code&gt; 的配置文件&lt;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门4.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202522794334901.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Properties\launchSettings.json&lt;/code&gt; 调试启动配置文件里面设置 DebugRoslynComponent 为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;commandName&lt;/code&gt; 内容。将要被调试的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JehairqogefaKaiwuwhailallkihaiki&lt;/code&gt; 控制台项目相对路径设置到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;targetProject&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;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;JehairqogefaKaiwuwhailallkihaiki.Analyzer&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;DebugRoslynComponent&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;targetProject&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;..&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;JehairqogefaKaiwuwhailallkihaiki&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;JehairqogefaKaiwuwhailallkihaiki.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;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;完成这些步骤之后，手工配置部分就完成了，即可愉快的在分析器项目打上断点，设置分析器项目为启动项目，然后直接在 Visual Studio 使用 F5 一键运行启动调试分析器项目&lt;/p&gt;

&lt;p&gt;如果大家发现自己的项目无法进行愉快的调试，可以尝试拉取我的代码用来测试和对比不同&lt;/p&gt;

&lt;p&gt;以上代码放在 &lt;a href=&quot;https://github.com/lindexi/lindexi_gd/tree/c0e948b2a3aab521f2d6d86593c385f4d406cfa5/Roslyn/JehairqogefaKaiwuwhailallkihaiki&quot;&gt;github&lt;/a&gt; 和 &lt;a href=&quot;https://gitee.com/lindexi/lindexi_gd/tree/c0e948b2a3aab521f2d6d86593c385f4d406cfa5/Roslyn/JehairqogefaKaiwuwhailallkihaiki&quot;&gt;gitee&lt;/a&gt; 上，可以使用如下命令行拉取代码。我整个代码仓库比较庞大，使用以下命令行可以进行部分拉取，拉取速度比较快&lt;/p&gt;

&lt;p&gt;先创建一个空文件夹，接着使用命令行 cd 命令进入此空文件夹，在命令行里面输入以下代码，即可获取到本文的代码&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;git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin c0e948b2a3aab521f2d6d86593c385f4d406cfa5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上使用的是国内的 gitee 的源，如果 gitee 不能访问，请替换为 github 的源。请在命令行继续输入以下代码，将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码，可以发邮件向我要代码&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;git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin c0e948b2a3aab521f2d6d86593c385f4d406cfa5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;获取代码之后，进入 Roslyn/JehairqogefaKaiwuwhailallkihaiki 文件夹，即可获取到源代码&lt;/p&gt;

&lt;h3 id=&quot;图形化的配置方式&quot;&gt;图形化的配置方式&lt;/h3&gt;

&lt;p&gt;有伙伴说每次都需要新建 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launchSettings.json&lt;/code&gt; 文件，要写相对的项目路径，这一点都不工程化，期望能够有更加方便的做法。我接下来将和大家介绍更加 UI 图形化的配置方式&lt;/p&gt;

&lt;p&gt;开始配置之前，请确保分析器项目正确配置了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IsRoslynComponent&lt;/code&gt; 属性，和被调试项目正确添加了分析器项目引用，配置了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OutputItemType=&quot;Analyzer&quot;&lt;/code&gt; 属性。细节配置还请参考上文的准备工作部分&lt;/p&gt;

&lt;p&gt;本文使用的 Visual Studio 为 Visual Studio 2022 17.12.4 版本。如果你的 Visual Studio 版本和我的差距过远，那可能以下图形界面或选项都有比较多的变更。这也就是为什么我选择先和大家介绍手工配置的原因&lt;/p&gt;

&lt;p&gt;配置步骤如下：&lt;/p&gt;

&lt;p&gt;先在 解决方案资源管理器 里面右击分析器项目，点击 设为启动项目 选项，将分析器项目设置为启动项目&lt;/p&gt;

&lt;p&gt;再点击分析器项目的调试属性，如下图所示&lt;/p&gt;

&lt;!-- ![](image/dotnet 在 VisualStudio 一键 F5 启动调试 Roslyn 分析器项目/dotnet 在 VisualStudio 一键 F5 启动调试 Roslyn 分析器项目0.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20252211054587914.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在打开的启动配置文件窗口里面，找个命令行参数，随便写入点字符。这个过程仅仅只是为了让 VisualStudio 帮助咱快速创建 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launchSettings.json&lt;/code&gt; 文件而已。我现在还没有找到比这个方法更加顺手便捷的方式哈&lt;/p&gt;

&lt;!-- ![](image/dotnet 在 VisualStudio 一键 F5 启动调试 Roslyn 分析器项目/dotnet 在 VisualStudio 一键 F5 启动调试 Roslyn 分析器项目1.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20252211056201460.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;双击 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Properties\launchSettings.json&lt;/code&gt; 文件进入编辑，现在可见的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launchSettings.json&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;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;JehairqogefaKaiwuwhailallkihaiki.Analyzer&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;commandLineArgs&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;123&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;language-plaintext highlighter-rouge&quot;&gt;commandName&lt;/code&gt; 属性的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Project&lt;/code&gt; 内容换成 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DebugRoslynComponent&lt;/code&gt; 内容，再删除 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;commandLineArgs&lt;/code&gt; 等其他属性。此时先不要写 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;targetProject&lt;/code&gt; 属性项，因为这个属性项要写相对路径，手写太烦了。编辑完成之后的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launchSettings.json&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;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;JehairqogefaKaiwuwhailallkihaiki.Analyzer&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;DebugRoslynComponent&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;!-- ![](image/dotnet 在 VisualStudio 一键 F5 启动调试 Roslyn 分析器项目/dotnet 在 VisualStudio 一键 F5 启动调试 Roslyn 分析器项目2.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202522111022690.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;愉快点击下拉菜单，选择要调试项目即可，如下图所示&lt;/p&gt;

&lt;!-- ![](image/dotnet 在 VisualStudio 一键 F5 启动调试 Roslyn 分析器项目/dotnet 在 VisualStudio 一键 F5 启动调试 Roslyn 分析器项目3.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2025221110546445.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;选中之后的效果如下图所示&lt;/p&gt;

&lt;!-- ![](image/dotnet 在 VisualStudio 一键 F5 启动调试 Roslyn 分析器项目/dotnet 在 VisualStudio 一键 F5 启动调试 Roslyn 分析器项目4.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2025221113511471.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;完成之后，再次打开 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launchSettings.json&lt;/code&gt; 文件，可以看到机智的 Visual Studio 已经帮咱填充了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;targetProject&lt;/code&gt; 属性内容了。通过 Visual Studio 的填充，可以让咱不需要写繁琐的相对路径，也不用担心写错项目路径导致调试出错&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;JehairqogefaKaiwuwhailallkihaiki.Analyzer&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;DebugRoslynComponent&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;targetProject&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;..&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;JehairqogefaKaiwuwhailallkihaiki&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;JehairqogefaKaiwuwhailallkihaiki.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;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;如配置完成运行失败，提示无法启动调试 0x80070057 错误，解决方法请参阅 &lt;a href=&quot;/post/dotnet-%E5%9C%A8-VisualStudio-%E4%B8%80%E9%94%AE-F5-%E5%90%AF%E5%8A%A8%E8%B0%83%E8%AF%95-Roslyn-%E5%88%86%E6%9E%90%E5%99%A8%E9%A1%B9%E7%9B%AE.html&quot;&gt;dotnet 在 VisualStudio 一键 F5 启动调试 Roslyn 分析器项目&lt;/a&gt;
&lt;!-- [dotnet 在 VisualStudio 一键 F5 启动调试 Roslyn 分析器项目 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18730521 ) --&gt;&lt;/p&gt;

&lt;h2 id=&quot;使用语法可视化窗格辅助了解语法&quot;&gt;使用语法可视化窗格辅助了解语法&lt;/h2&gt;

&lt;p&gt;有些伙伴会感觉即使在有上文的调试方法辅助的情况下，编写语法分析还是太复杂了，不知道怎么写。自己对语法分析本身也不熟悉，不知道可以如何编写语法分析的代码。这个时候可以使用视觉辅助了解语法&lt;/p&gt;

&lt;p&gt;在 Visual Studio 里面自带了语法可视化（Syntax Visualizer）功能，可以帮助大家更加直观的了解代码的语法树。在 Visual Studio 里面打开一个 C# 文件，然后在菜单栏里面点击 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;View（视图）&lt;/code&gt; -&amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Other Windows（其他窗口）&lt;/code&gt; -&amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Syntax Visualizer&lt;/code&gt; 打开语法可视化窗格，如下图所示&lt;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门6.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20252281923515265.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;其界面大概如下&lt;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门5.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20252281922443223.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果没有从视图里面找到 Syntax Visualizer 语法可视化窗格，则需要给 Visual Studio 打上 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.NET Compiler Platform SDK&lt;/code&gt; 负载。正常来说，根据上文的步骤一步步来的伙伴，都在前面准备直接调试的过程里面已经安装好了这个负载。安装方法如下：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;运行“Visual Studio 安装程序”&lt;/li&gt;
  &lt;li&gt;选择“修改”&lt;/li&gt;
  &lt;li&gt;检查“Visual Studio 扩展开发”工作负荷。&lt;/li&gt;
  &lt;li&gt;在摘要树中打开“Visual Studio 扩展开发”节点。&lt;/li&gt;
  &lt;li&gt;选中“.NET Compiler Platform SDK”框。 将在可选组件最下面找到它&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;详细安装方法请参阅 &lt;a href=&quot;https://learn.microsoft.com/zh-cn/dotnet/csharp/roslyn-sdk/syntax-visualizer&quot;&gt;使用 Visual Studio 中的 Roslyn 语法可视化工具浏览代码 - C# - Microsoft Learn&lt;/a&gt; 官方文档&lt;/p&gt;

&lt;p&gt;回顾语法可视化窗格界面，可以看到有多个颜色标注出来不同的语法节点，如下图所示&lt;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门6.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20252281923515265.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;蓝色：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SyntaxNode&lt;/code&gt;，表示声明、语句、子句和表达式等语法构造。&lt;/li&gt;
  &lt;li&gt;绿色：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SyntaxToken&lt;/code&gt;，表示关键字、标识符、运算符等标点。&lt;/li&gt;
  &lt;li&gt;红色：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SyntaxTrivia&lt;/code&gt;，代表语法上不重要的信息，例如标记、预处理指令和注释之间的空格。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;通过对照语法可视化窗格，可以更加直观的了解代码的语法树结构，从而更好的编写语法分析代码。在编写语法分析代码的时候，可以通过语法可视化窗格辅助了解语法，更加直观的了解代码的语法树结构，根据语法树结构编写语法分析代码&lt;/p&gt;

&lt;p&gt;更多关于使用 Visual Studio 的语法可视化（Syntax Visualizer）窗格方法，请参阅：
&lt;a href=&quot;https://blog.walterlv.com/post/roslyn-syntax-visualizer&quot;&gt;Roslyn 入门：使用 Visual Studio 的语法可视化（Syntax Visualizer）窗格查看和了解代码的语法树 - walterlv&lt;/a&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;假定有 F1 和 F2 和 F3 三个类型，其定义代码分别如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;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;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;F1&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;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;F1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IContext&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;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;F2&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;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;F2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IContext&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;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;F3&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;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;F3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IContext&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;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;IContext&lt;/span&gt;
&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;static&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;FooCollection&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&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;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&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;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IContext&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;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetFooCreatorList&lt;/span&gt;&lt;span class=&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;context&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;F1&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;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;context&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;F2&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;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;context&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;F3&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上的 FooCollection 的 GetFooCreatorList 方法就是咱源代码生成器的生成任务内容。这是一个知识内容比较综合的演练。我将在这个演练里面和大家演示源代码生成器的日常食用方法&lt;/p&gt;

&lt;p&gt;假定现在用户已经定义好了 F1 和 F2 和 F3 三个类型，被其继承的 IFoo 接口和用作参数的 IContext 接口，以及如下代码所示的 FooCollection 的分部 GetFooCreatorList 方法。源代码生成器需要生成 CollectionAttribute 特性类型的代码，以及 FooCollection 的 GetFooCreatorList 分部方法的具体实现代码&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FooCollection&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&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;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&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;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IContext&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;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetFooCreatorList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;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;h3 id=&quot;演练步骤&quot;&gt;演练步骤&lt;/h3&gt;

&lt;p&gt;整体的步骤可以分为以下几个步骤：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;生成 CollectionAttribute 特性类型的代码&lt;/li&gt;
  &lt;li&gt;分析使用了 CollectionAttribute 特性的分部方法，且找到方法的返回值参数&lt;/li&gt;
  &lt;li&gt;根据返回值参数的类型，遍历收集项目的类型，找到感兴趣的类型，生成创建器代码&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;依然是为了方便大家获取到正确的源代码，我这里重新创建两个项目，分别是名为 KawhawnahemCanalllearlerwhu 的控制台项目，以及名为 KawhawnahemCanalllearlerwhu.Analyzer 的分析器项目。这两个项目的初始化搭建和上文的章节一样，不再赘述。大家可以直接使用上文的章节的代码进行初始化搭建&lt;/p&gt;

&lt;p&gt;完成项目搭建之后，就可以开始进入本次演练的步骤了&lt;/p&gt;

&lt;p&gt;演练内容里面只给出关键代码片段，如需要全部的项目文件，可到本章末尾找到所有代码的拉取下载方法&lt;/p&gt;

&lt;h4 id=&quot;生成特性类型的代码&quot;&gt;生成特性类型的代码&lt;/h4&gt;

&lt;p&gt;生成 CollectionAttribute 特性类型的代码部分，可以参考上文的章节，这里不再赘述。直接使用 RegisterPostInitializationOutput 方法注册生成 CollectionAttribute 特性类型的代码&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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.Buffers&lt;/span&gt;&lt;span class=&quot;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.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.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.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&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;KawhawnahemCanalllearlerwhu.Analyzer.Properties&lt;/span&gt;&lt;span class=&quot;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;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;KawhawnahemCanalllearlerwhu.Analyzer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Generator&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;FooIncrementalGenerator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IIncrementalGenerator&lt;/span&gt;
&lt;span class=&quot;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;IncrementalGeneratorInitializationContext&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;RegisterPostInitializationOutput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;initializationContext&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;initializationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CollectionAttribute.cs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;
&lt;/span&gt;                &lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Lindexi&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;CollectionAttribute&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;span class=&quot;s&quot;&gt;&quot;&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;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;以上代码唯一的细节是设置 CollectionAttribute 为 internal 类型，这样就可以保证 CollectionAttribute 只能在当前项目内部使用，不会被外部项目引用到。如此能够规避多个相互引用的项目同时使用了此分析器，导致生成了多个相同命名空间的 CollectionAttribute 类型的问题&lt;/p&gt;

&lt;h4 id=&quot;分析使用了-collectionattribute-特性的分部方法&quot;&gt;分析使用了 CollectionAttribute 特性的分部方法&lt;/h4&gt;

&lt;p&gt;使用上文章节的 ForAttributeWithMetadataName 方法找到标记了 CollectionAttribute 特性的方法。这里需要说明的是 ForAttributeWithMetadataName 方法不仅可以用来找类型，还可以用来找其他可以标记特性的成员，自然也包括方法&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ForAttributeWithMetadataName&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;&quot;Lindexi.CollectionAttribute&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxNode&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;CancellationToken&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;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;if&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;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MethodDeclarationSyntax&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methodDeclarationSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;// 判断是否是 partial 分部方法&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methodDeclarationSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Modifiers&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;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;.&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;PartialKeyword&lt;/span&gt;&lt;span class=&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;n&quot;&gt;GeneratorAttributeSyntaxContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxContext&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;_&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;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;在 C# dotnet 里面的分部方法的设计上，可以让源代码生成器和 IDE 都非常开森。其原因是在 IDE 的视角上，分部方法已经完成了整个方法的对外定义。对于其他外部的引用来说，已经满足了基础的符号关系。毕竟对于外部引用来说，具体方法里面的实现是完全不关心的。只要有方法定义，就可以完全建立符号关系。这就意味着具体分部方法的实现代码，可以慢慢让源代码生成器来生成，在源代码生成器生成的过程中，IDE 也不会有任何的报错飘红。对源代码生成器来说，分部方法是一个非常好的锚点，特别是加上标记了特性的分部方法。这就是为什么现在很多 dotnet 基础支持上，都推荐写分部方法标记特性来实现很多功能的原因，比如以下代码演示的 GeneratedRegex 正则表达式源生成器方法&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;GeneratedRegex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;abc|def&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;IgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;en-US&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;partial&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Regex&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AbcOrDefGeneratedRegex&lt;/span&gt;&lt;span class=&quot;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;EvaluateText&lt;/span&gt;&lt;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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AbcOrDefGeneratedRegex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsMatch&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;// Take action with matching 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;以上代码为 dotnet 内建机制，可以有效生成高速的 Regex 代码，极大提升整体性能，避免运行时编正则带来的损耗，如对此细节感兴趣，请参阅 &lt;a href=&quot;https://learn.microsoft.com/zh-cn/dotnet/standard/base-types/regular-expression-source-generators&quot;&gt;.NET 正则表达式源生成器 - .NET - Microsoft Learn&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;以上举例的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AbcOrDefGeneratedRegex&lt;/code&gt; 仅仅只是歪楼告诉大家，分部方法配合特性，让源代码生成器填充具体实现内容是现在 dotnet 的惯用方法而已。举例的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AbcOrDefGeneratedRegex&lt;/code&gt; 以及正则内容和本文内容没有直接关联&lt;/p&gt;

&lt;p&gt;在 ForAttributeWithMetadataName 的语义转换步骤里面，将获取其分部方法的返回值类型，以及在此同时生成部分代码&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;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ForAttributeWithMetadataName&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;&quot;Lindexi.CollectionAttribute&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxNode&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;CancellationToken&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;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;GeneratorAttributeSyntaxContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxContext&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;_&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;methodSymbol&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IMethodSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;syntaxContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TargetSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;methodSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsPartialDefinition&lt;/span&gt;&lt;span class=&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;n&quot;&gt;ITypeSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;returnType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methodSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReturnType&lt;/span&gt;&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;// 预期是 IEnumerable&amp;lt;Func&amp;lt;IContext, IFoo&amp;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;returnType&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;INamedTypeSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methodSymbolReturnType&lt;/span&gt;&lt;span class=&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;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;ITypeSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;returnType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methodSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReturnType&lt;/span&gt;&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;// 预期是 IEnumerable&amp;lt;Func&amp;lt;IContext, IFoo&amp;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;returnType&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;INamedTypeSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methodSymbolReturnType&lt;/span&gt;&lt;span class=&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;fullNameDisplayFormat&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SymbolDisplayFormat&lt;/span&gt;
 &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;SymbolDisplayGlobalNamespaceStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Included&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
     &lt;span class=&quot;c1&quot;&gt;// 命名空间之前加上 global 防止冲突&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;SymbolDisplayTypeQualificationStyle&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NameAndContainingTypesAndNamespaces&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
 &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;returnTypeName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methodSymbolReturnType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToDisplayString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fullNameDisplayFormat&lt;/span&gt;&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;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exceptedReturnTypeName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;global::System.Collections.Generic.IEnumerable&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;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;returnTypeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exceptedReturnTypeName&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;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;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;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;methodSymbolReturnType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TypeArguments&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;c1&quot;&gt;// 预期是 IEnumerable&amp;lt;Func&amp;gt; 这样的类型，在 IEnumerable 里面只有一个泛型参数&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;c1&quot;&gt;// 取出 IEnumerable&amp;lt;Func&amp;lt;IContext, IFoo&amp;gt;&amp;gt; 中的 Func&amp;lt;IContext, IFoo&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;methodSymbolReturnType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TypeArguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;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;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INamedTypeSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;funcTypeSymbol&lt;/span&gt;&lt;span class=&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;language-plaintext highlighter-rouge&quot;&gt;funcTypeSymbol&lt;/code&gt; 变量也要判断一下是否 System.Func 类型，以及判断其参数是否符合预期&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;// 取出 IEnumerable&amp;lt;Func&amp;lt;IContext, IFoo&amp;gt;&amp;gt; 中的 Func&amp;lt;IContext, IFoo&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;methodSymbolReturnType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TypeArguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;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;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INamedTypeSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;funcTypeSymbol&lt;/span&gt;&lt;span class=&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;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exceptedFuncTypeName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;global::System.Func&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;funcTypeName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;funcTypeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToDisplayString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fullNameDisplayFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;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;funcTypeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exceptedFuncTypeName&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;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;c1&quot;&gt;// 如果不是 Func 类型的，则不是预期的&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;c1&quot;&gt;// 继续取出 Func&amp;lt;IContext, IFoo&amp;gt; 中的 IContext 和 IFoo 部分&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;funcTypeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TypeArguments&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;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;language-plaintext highlighter-rouge&quot;&gt;IEnumerable&amp;lt;Func&amp;lt;IContext, IFoo&amp;gt;&amp;gt;&lt;/code&gt; 类型返回值，以及取出 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IContext&lt;/code&gt; 作为参数类型和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IFoo&lt;/code&gt; 作为返回值类型。虽然代码看起来很长，但相信大家能够很快理解&lt;/p&gt;

&lt;p&gt;以下为取出 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IContext&lt;/code&gt; 作为参数类型和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IFoo&lt;/code&gt; 作为返回值类型的代码，后续逻辑将需要用到这两个类型的语义&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;// 取出 Func&amp;lt;IContext, IFoo&amp;gt; 中的 IContext 部分&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;ITypeSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;constructorArgumentType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;funcTypeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TypeArguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;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;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;constructorArgumentTypeName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;constructorArgumentType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToDisplayString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fullNameDisplayFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
 &lt;span class=&quot;c1&quot;&gt;// 取出 Func&amp;lt;IContext, IFoo&amp;gt; 中的 IFoo 部分&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;ITypeSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;collectionType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;funcTypeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TypeArguments&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;collectionTypeName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;collectionType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToDisplayString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fullNameDisplayFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上代码在取出的过程中，顺带也获取类型的全名，这在后续的代码生成过程中会用到。在这个步骤里面就立刻生成了部分的代码。这是因为在这里进行生成，可以省去将当前的 IMethodSymbol 传递到后续的代码生成过程中，提升不到一分钱的性能&lt;/p&gt;

&lt;p&gt;在 ForAttributeWithMetadataName 的 transform 过程中，作为返回值的内容，都会参与到缓存的计算中。在增量源代码生成设计里面，通过大量的缓存换取减少计算的时间。但缓存本身会涉及很多相等判断逻辑，传递 IMethodSymbol 等符号对象在判断中会比传递字符串更加昂贵，这就是为什么即刻在此进行消费的原因。但这里需要取得一个平衡点，更多发出转换器的代码，而不要在一个转换器里面写太多逻辑，减少变更代码过程中的无效逻辑处理，防止跑了一大堆逻辑但最终因为代码文件内容变更而无效的情况&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;c1&quot;&gt;// 生成的代码的示例内容&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;KawhawnahemCanalllearlerwhu&lt;/span&gt;&lt;span class=&quot;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;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FooCollection&lt;/span&gt;
&lt;span class=&quot;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;partial&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;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IContext&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;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetFooCreatorList&lt;/span&gt;&lt;span class=&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;context&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;F1&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;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;context&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;F2&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;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;context&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;F3&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然了，其中间的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield return context =&amp;gt; new F1(context);&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;INamedTypeSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;containingType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methodSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContainingType&lt;/span&gt;&lt;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;classNamespace&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;containingType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContainingNamespace&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;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;n&quot;&gt;containingType&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;// Modifiers&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;Accessibility&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;declaredAccessibility&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;containingType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DeclaredAccessibility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;modifier&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AccessibilityToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;declaredAccessibility&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;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AccessibilityToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Accessibility&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accessibility&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;accessibility&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;Accessibility&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;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;public&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Accessibility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Protected&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;protected&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;_&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;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;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门15.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2025317855425080.jpg&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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generatedCode&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;&quot;&quot;
&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;classNamespace&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;modifier&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;containingType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsStatic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; static&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;err&quot;&gt;｝｝&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;err&quot;&gt;｛｛&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;className&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;nf&quot;&gt;AccessibilityToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;methodSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DeclaredAccessibility&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;methodSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsStatic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; static&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;err&quot;&gt;｝｝&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;｛｛&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exceptedReturnTypeName&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;｝｝&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;｛｛&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exceptedFuncTypeName&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;｝｝&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;｛｛&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;constructorArgumentTypeName&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;collectionTypeName&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;｝｝&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;err&quot;&gt;｛｛&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;methodSymbol&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;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;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&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;F1&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;s&quot;&gt;&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;/p&gt;

&lt;p&gt;以上空壳框架代码的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield return context =&amp;gt; new F1(context);&lt;/code&gt; 将在后续用作替换字符串的占位符，当前生成的代码内容，即 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;generatedCode&lt;/code&gt; 变量的字符串内容如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;KawhawnahemCanalllearlerwhu&lt;/span&gt;&lt;span class=&quot;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;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FooCollection&lt;/span&gt;
&lt;span class=&quot;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;partial&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;Collections&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;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&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;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&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;KawhawnahemCanalllearlerwhu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IContext&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;KawhawnahemCanalllearlerwhu&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;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;GetFooCreatorList&lt;/span&gt;&lt;span class=&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;context&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;F1&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在源代码生成器的套路里面，就是尽量使用全命名空间，即带上 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;global::&lt;/code&gt; 前缀，这样可以避免引用冲突。在这里的代码生成过程中，也是使用了全命名空间的方式，以保证生成的代码可以在任何地方使用。虽然这个方式会让生成的代码比较繁琐，但毕竟是机器生成的代码，不需要人工去编写，只是会添加一些阅读的心智负担&lt;/p&gt;

&lt;p&gt;如果感觉确实阅读不方便，那就在 using 处写明别名，带上全命名空间，如 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;using Xxx = global::Xx.Fxxx&lt;/code&gt; 之类的写法&lt;/p&gt;

&lt;p&gt;为了在 ForAttributeWithMetadataName 的 transform 进行返回，这里定义一个名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CollectionExportMethodInfo&lt;/code&gt; 的类型，用于存储过程信息，其代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CollectionExportMethodInfo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ITypeSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConstructorArgumentType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ITypeSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CollectionType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;GeneratedCodeInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GeneratedCodeInfo&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;n&quot;&gt;Location&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;GeneratedCodeInfo&lt;/span&gt;&lt;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;GeneratedCode&lt;/span&gt;&lt;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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在源代码生成器里面使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;record&lt;/code&gt; 或 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;readonly record struct&lt;/code&gt; 是非常舒坦的，因为记录类型自带了相等判断比较器，可以省去很多工作量。但在这里需要额外说明的是，默认的相等比较器对符号类型来说是不够准确的，有心的源代码生成器开发者可以对以上的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CollectionExportMethodInfo&lt;/code&gt; 类型进行更加准确的相等比较器的重写，使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SymbolEqualityComparer&lt;/code&gt; 比较器代替默认的相等比较器。这里的核心原因是 Roslyn 在设计之初时， C# 代码还没有可空的概念。于是设计上对类型只有一个概念，后续 NRT (Nullable Reference Types) 引入之后，导致了一个类型还有另一个可空概念，进而导致了判断逻辑上存在两个选项，分别是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SymbolEqualityComparer.Default&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SymbolEqualityComparer.IncludeNullability&lt;/code&gt; 这两个选项。为了明确起见，于是 Roslyn 团队决定引入 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SymbolEqualityComparer&lt;/code&gt; 比较器，从而可以让分析器开发者明确知道自己在做什么&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SymbolEqualityComparer.Default&lt;/code&gt; 比较器是不包含可空性的比较器，即不区分可空性的比较器。对 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;string&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;string?&lt;/code&gt; 进行相等比较，返回的结果是相等的。这个比较器是默认的比较器，与默认会调用的相等比较器行为相同。这就是为什么上述代码即使不重写相等比较器，在业务上也是正确的原因&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SymbolEqualityComparer.IncludeNullability&lt;/code&gt; 比较器是包含可空性的比较器，即区分可空性的比较器。对 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;string&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;string?&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;n&quot;&gt;ISymbol&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;n&quot;&gt;ISymbol&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;defaultAreEquals&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;Equals&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;// Warn: RS1024 Symbols should be compared for equality&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;areEquals&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SymbolEqualityComparer&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;nf&quot;&gt;Equals&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;c1&quot;&gt;// string == string?&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;areEquals&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SymbolEqualityComparer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IncludeNullability&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;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;c1&quot;&gt;// string != string?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注： 更多关于 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SymbolEqualityComparer&lt;/code&gt; 比较器与默认比较器的差别，请参阅此帖子： &lt;a href=&quot;https://github.com/dotnet/roslyn-analyzers/issues/3427&quot;&gt;https://github.com/dotnet/roslyn-analyzers/issues/3427&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;完成 CollectionExportMethodInfo 的定义之后，将其作为返回值返回&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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 Studio 应该在哪里飘红&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&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;syntaxContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TargetNode&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;c1&quot;&gt;// 使用 record 类型自带的相等判断，能够省心很多&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;CollectionExportMethodInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;constructorArgumentType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;collectionType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GeneratedCodeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;generatedCode&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;className&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;methodSymbol&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;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;返回时，从 TargetNode 里面调用 GetLocation 获取到 Location 位置信息。此 Location 信息可用于后续生成警告和错误信息，即告诉 Visual Studio 应该在哪里飘红。拿到的 Location 就是对应的代码的位置信息，如是哪个文件，哪个行号，从哪列到哪列等信息&lt;/p&gt;

&lt;p&gt;由于在 ForAttributeWithMetadataName 语义分析过程中，还包含了一些过滤条件，将不满足条件的，都使用 null 进行返回。于是在 ForAttributeWithMetadataName 方法完成返回时，再叠加 Where 条件，用于过滤掉不符合条件的情况。其代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;provider&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;SyntaxProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ForAttributeWithMetadataName&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;&quot;Lindexi.CollectionAttribute&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxNode&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;CancellationToken&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;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;GeneratorAttributeSyntaxContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxContext&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;_&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;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;c1&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;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;!=&lt;/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;回顾本次演练的任务，在当前步骤里面收集到的是一个个的标记了 CollectionAttribute 特性的分部方法，以及这些方法的返回值类型。敲黑板，这里收集到的是一个个的。这就意味着如果直接拿这一个个去与后续的全项目所有类型进行处理，则其处理次数会是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m * n&lt;/code&gt; 的量，这里的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m&lt;/code&gt; 是标记了 CollectionAttribute 特性的分部方法的数量，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;n&lt;/code&gt; 是全项目所有类型的数量。且这个触发不止一次，而是每次有代码变更都会触发。在 Roslyn 源代码生成器里面禁止此行为，只允许将 IncrementalValuesProvider 多值提供器与 IncrementalValueProvider 单值提供器进行 Combine 组合。禁止将 IncrementalValuesProvider 多值提供器与 IncrementalValuesProvider 多值提供器进行组合&lt;/p&gt;

&lt;p&gt;这里有一个容易混淆的点，多值提供器与单值提供器，其类型分别如下：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValuesProvider&amp;lt;T&amp;gt;&lt;/code&gt; 多值提供器&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValueProvider&amp;lt;T&amp;gt;&lt;/code&gt; 单值提供器&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;两者差异只是一个是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Values&lt;/code&gt; 而另一个是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Value&lt;/code&gt; 而已，即多了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;s&lt;/code&gt; 的差别而已，两个单词比较好混哦&lt;/p&gt;

&lt;p&gt;为了能够和后续的全项目类型收集进行 Combine 合并组合处理，这里在 Where 之后，再调用 Collect 方法，将其收集起来，成为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValueProvider&amp;lt;ImmutableArray&amp;lt;CollectionExportMethodInfo&amp;gt;&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;n&quot;&gt;IncrementalValueProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&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;CollectionExportMethodInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;collectionMethodInfoProvider&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;SyntaxProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ForAttributeWithMetadataName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;&quot;Lindexi.CollectionAttribute&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxNode&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;CancellationToken&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;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;GeneratorAttributeSyntaxContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxContext&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;_&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;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;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;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;!=&lt;/span&gt; &lt;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;Collect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()!;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;大家是否好奇，似乎这里的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValueProvider&amp;lt;ImmutableArray&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt; 单值提供器也是骗人的，里面明明就是一个不可变数组，也就是里面就是一个集合。为什么这样也能称为单值提供器？因为多值和单值是从源代码生成器的缓存角度来说的。即数据提供器里面提供的是多个值还是单个值。这里的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValueProvider&amp;lt;ImmutableArray&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt; 单值提供器，其提供的是一个集合，即一个值，所以称为单值提供器。核心差异在于如代码变更的时候，应该刷新的范围是多大。对于 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValueProvider&amp;lt;ImmutableArray&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt; 来说，只要有一个标记了 CollectionAttribute 的符合条件的分部方法发生了变更，就会触发整个集合的刷新，即 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;collectionMethodInfoProvider&lt;/code&gt; 将会重新提供值&lt;/p&gt;

&lt;p&gt;但对于 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValuesProvider&amp;lt;T&amp;gt;&lt;/code&gt; 多值提供器来说，里面的每一项都是独立的，其中一项的变更，只有触发其对应的一次，而不会影响其他项的触发。这也就是为什么 Collect 的设计上不允许多值提供器与多值提供器进行组合的原因。因为多值提供器与多值提供器组合，将会在某一项值变更的时候，其触发条件是比较震荡的，复杂度比较高，不仅人类程序猿顶不住，机器也顶不住&lt;/p&gt;

&lt;p&gt;换句话说就是只要任意一个标记了 CollectionAttribute 的符合条件的分部方法发生了变更，就会触发整个 ImmutableArray 集合的刷新。但任意一个标记了 CollectionAttribute 的符合条件的分部方法发生了变更，走到 Where 处的变更也就只有这一个分部方法而已，只不过后续的 ImmutableArray 集合的刷新是靠 Collect 触发的。因为到 Where 处还是多值提供器，只有到 Collect 处才会变成单值提供器&lt;/p&gt;

&lt;p&gt;以上代码就完成了对标记了 CollectionAttribute 特性的分部方法的收集，分析使用了 CollectionAttribute 特性的分部方法，且找到方法的返回值参数，生成 CollectionAttribute 特性类型的代码。接下来将会在下一步根据返回值参数的类型，遍历收集全项目的类型，找到感兴趣的类型，生成创建器代码&lt;/p&gt;

&lt;h4 id=&quot;遍历收集全项目的类型生成创建器代码&quot;&gt;遍历收集全项目的类型，生成创建器代码&lt;/h4&gt;

&lt;p&gt;全项目类型收集过程里面将不能使用 ForAttributeWithMetadataName 方法，而是需要使用上文介绍的高度定制支持的更底层的收集分析的 CreateSyntaxProvider 方法。在语法层面，先判断是类型即可通过，本身就需要遍历全项目的类型的，自然判断语法是类型即可&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;IncrementalValuesProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INamedTypeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wholeAssemblyClassTypeProvider&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;SyntaxProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateSyntaxProvider&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;n&quot;&gt;SyntaxNode&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;CancellationToken&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;node&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;ClassDeclaration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratorSyntaxContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxContext&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;_&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;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;语义层面上，由于现在还没有和对标记了 CollectionAttribute 特性的分部方法的收集的合并，在语义层面上也就没啥好判断的。最多只判断要求类型不能是抽象的，毕竟按照咱的需求任务来说，要的就是创建出对象，抽象类型就不能被直接创建啦，自然就可以被过滤掉&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;IncrementalValuesProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INamedTypeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wholeAssemblyClassTypeProvider&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;SyntaxProvider&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateSyntaxProvider&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;n&quot;&gt;SyntaxNode&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;CancellationToken&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;node&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;ClassDeclaration&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;n&quot;&gt;GeneratorSyntaxContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generatorSyntaxContext&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;token&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;classDeclarationSyntax&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClassDeclarationSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generatorSyntaxContext&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;INamedTypeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assemblyClassTypeSymbol&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;generatorSyntaxContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SemanticModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetDeclaredSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;classDeclarationSyntax&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;assemblyClassTypeSymbol&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;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;assemblyClassTypeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAbstract&lt;/span&gt;&lt;span class=&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;assemblyClassTypeSymbol&lt;/span&gt;&lt;span class=&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;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;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;!=&lt;/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;完成了全项目类型的收集之后，就可以和收集了标记了 CollectionAttribute 特性的分部方法的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;collectionMethodInfoProvider&lt;/code&gt; 进行合并，其代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;wholeAssemblyClassTypeProvider&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;collectionMethodInfoProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;调用 Combine 之后返回的类型是一个元组，为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValuesProvider&amp;lt;(INamedTypeSymbol Left, ImmutableArray&amp;lt;CollectionExportMethodInfo&amp;gt; Right)&amp;gt;&lt;/code&gt; 类型。即左边 Left 是多值提供器里面的每一个值，即项目里面的每个类型，右边是单值提供器里面的值&lt;/p&gt;

&lt;!-- 实际效果就是当项目里面的单个代码文件变更的时候，只触发一次，且这一次会和单值提供器的值重跑一遍。 --&gt;

&lt;p&gt;继续处理，带上 Select 方法，判断各自类型是否满足标记了 CollectionAttribute 特性的分部方法的感兴趣条件&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;wholeAssemblyClassTypeProvider&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;collectionMethodInfoProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&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;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INamedTypeSymbol&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;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CollectionExportMethodInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&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;n&quot;&gt;tuple&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;token&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;INamedTypeSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assemblyClassTypeSymbol&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tuple&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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exportMethodReturnTypeCollectionResultArray&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tuple&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;// 忽略其他代码&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在这一步里面，咱可以激进一些，直接就干到生成了对应的项的代码里面，即生成如 ` yield return context =&amp;gt; new Foo(context);` 的代码。为了表示此返回类型，这里再次定义一个名为 ItemGeneratedCodeResult 的新的类型&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ItemGeneratedCodeResult&lt;/span&gt;
&lt;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;ItemGeneratedCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;GeneratedCodeInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExportMethodGeneratedCodeInfo&lt;/span&gt;
&lt;span class=&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;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;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&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;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个新的 ItemGeneratedCodeResult 类型采用的是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;readonly record struct&lt;/code&gt; 的设计，这会让分析器更加开森。我感觉 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;readonly record struct&lt;/code&gt; 是非常舒坦的设计，不会担心这样的类型在大量使用中，会造成大量的堆对象分配，也不会担心其分配成本和 GC 压力。使用值类型的设计是在分析器官方里面所推荐的，如以下的官方文档所示&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Use value types where possible: Value types are more amenable to caching and usually have well defined and easy to understand comparison semantics.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;以上的 ItemGeneratedCodeResult 类型包含了 Diagnostic 类型的 Diagnostic 属性，这是用于在进行源代码生成过程中，发现某些代码不符合预期，进行的分析警告或错误信息。从这里也可以看出来源代码生成器本身也带有分析器的功能，这部分的具体使用将在下文介绍&lt;/p&gt;

&lt;p&gt;由于 Right 是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImmutableArray&amp;lt;CollectionExportMethodInfo&amp;gt;&lt;/code&gt; 类型，表示的所有的标记了 CollectionAttribute 的分部方法收集信息。因此这里咱也需要对应的创建一个列表，用于建立多对多的关系，即一个类型可能存在对应多个分部方法的关系&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;Select&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;n&quot;&gt;INamedTypeSymbol&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;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CollectionExportMethodInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&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;n&quot;&gt;tuple&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;token&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;INamedTypeSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assemblyClassTypeSymbol&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tuple&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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exportMethodReturnTypeCollectionResultArray&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tuple&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;c1&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;n&quot;&gt;ItemGeneratedCodeResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;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;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;language-plaintext highlighter-rouge&quot;&gt;List&amp;lt;ItemGeneratedCodeResult&amp;gt;? result&lt;/code&gt; 我选择不要一开始就创建，因为现在收集到的类型不一定会满足任何一个分部方法的要求，即这将是一个被忽略的类型。慢点创建可以减少浪费&lt;/p&gt;

&lt;p&gt;遍历分部方法收集 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImmutableArray&amp;lt;CollectionExportMethodInfo&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;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;static&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INamedTypeSymbol&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;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CollectionExportMethodInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&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;n&quot;&gt;tuple&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;token&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;INamedTypeSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assemblyClassTypeSymbol&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tuple&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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exportMethodReturnTypeCollectionResultArray&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tuple&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;c1&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;n&quot;&gt;ItemGeneratedCodeResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;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;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CollectionExportMethodInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exportMethodInfo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exportMethodReturnTypeCollectionResultArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;// 一般进入循环的时候，都会加上这个判断。这个判断逻辑的作用是如开发者在 IDE 里面进行编辑文件的时候，那此文件对应的类型就需要重新处理，即类型对应的 token 将会被激活。此时在循环跑的逻辑就是浪费的，逻辑需要重跑，因此需要判断 token 是否被取消，减少循环里面的不必要的逻辑损耗&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;// check for cancellation so we don&apos;t hang the host&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;ThrowIfCancellationRequested&lt;/span&gt;&lt;span class=&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;按照 Roslyn 的设计，在进入大循环等逻辑时，应该多判断一下令牌。这个原因是开发者可能不断在 IDE 里面进行编辑文件，源代码生成器执行过程中对应的文件已经被更改了，本次处理是无效的，此时对此文件涉及的相关类型的处理就应该无效掉，等待重新进入。预先多加令牌判断，可以减少无用处理，减少损耗，避免原本就很卡的 Visual Studio 更加卡顿&lt;/p&gt;

&lt;p&gt;在 foreach 里面判断当前的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;assemblyClassTypeSymbol&lt;/code&gt; 类型是否继承自分部方法要求的返回类型，如以下代码所示&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;n&quot;&gt;CollectionExportMethodInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exportMethodInfo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exportMethodReturnTypeCollectionResultArray&lt;/span&gt;&lt;span class=&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;ThrowIfCancellationRequested&lt;/span&gt;&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;IsInherit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assemblyClassTypeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exportMethodInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CollectionType&lt;/span&gt;&lt;span class=&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;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;以上的 IsInherit 方法的实现如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;currentType&quot;&amp;gt;当前的类型&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;requiredType&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;IsInherit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ITypeSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ITypeSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requiredType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&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;n&quot;&gt;currentType&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;k&quot;&gt;while&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;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;not&lt;/span&gt; &lt;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;SymbolEqualityComparer&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;nf&quot;&gt;Equals&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;n&quot;&gt;requiredType&lt;/span&gt;&lt;span class=&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;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;baseType&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;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;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;currentInheritInterfaceType&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AllInterfaces&lt;/span&gt;&lt;span class=&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;SymbolEqualityComparer&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;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;currentInheritInterfaceType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requiredType&lt;/span&gt;&lt;span class=&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;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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;继承条件判断里面是无视引用对象可空情况的，直接使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SymbolEqualityComparer.Default&lt;/code&gt; 判断即可，不用或不该用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SymbolEqualityComparer.IncludeNullability&lt;/code&gt; 进行判断。以上的 IsInherit 是一个我常写的工具方法，可以用来判断给定类型是否被继承，包括基类型和接口类型&lt;/p&gt;

&lt;p&gt;在真实项目里面，通过 IsInherit 即可过滤大量类型，毕竟能够满足条件的，预期还是少数。再下一步就是寻找构造函数了。在 C# 语法里面，只能做到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;new T()&lt;/code&gt; 泛型，做不到构造函数里面带参数的情况。源代码生成器里面可以轻易做到这一点，通过这个演练也能让大家看到源代码生成器的威力。在很多通用创建器、工厂模式等，可以打破泛型 T 只能创建无参构造函数的限制，过程中也不用任何反射，都是最直接的代码，对裁剪和 AOT 友好&lt;/p&gt;

&lt;p&gt;如果类型满足继承条件，则继续寻找构造函数。感兴趣的构造函数的特征是有且只有一个参数，参数类型等于分部方法传入的 context 类型。我这里就完全限定参数类型相等，而不是说其 context 的基类型也可以，这仅仅只是为了简单演示而已&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;nf&quot;&gt;IsInherit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assemblyClassTypeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exportMethodInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CollectionType&lt;/span&gt;&lt;span class=&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;c1&quot;&gt;// 遍历其构造函数，找到感兴趣的构造函数&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IMethodSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;candidateConstructorMethodSymbol&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;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IMethodSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;constructorMethodSymbol&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ssemblyClassTypeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Constructors&lt;/span&gt;&lt;span class=&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;constructorMethodSymbol&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;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;c1&quot;&gt;// 根据需求任务可知，感兴趣的构造函数的特征是有且只有一个参数&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 如果参数数量不等于 1 则不满足条件&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;c1&quot;&gt;// 判断参数的类型是否符合预期&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IParameterSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameterSymbol&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;constructorMethodSymbol&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;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;kt&quot;&gt;var&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;parameterSymbol&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;c1&quot;&gt;// 以下忽略是否可空的判断，因此业务上传入时都是有值的，因此无视可空情况。直接使用 SymbolEqualityComparer.Default 判断即可&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SymbolEqualityComparer&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;nf&quot;&gt;Equals&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;exportMethodInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConstructorArgumentType&lt;/span&gt;&lt;span class=&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;candidateConstructorMethodSymbol&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;constructorMethodSymbol&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;c1&quot;&gt;// 当然，如果前面判断条件开放为判断满足 `exportMethodInfo.ConstructorArgumentType` 的基类型条件，那自然这里也许就可能会有多个构造函数的情况，也就需要排优先级了哈。不排也可以，毕竟生成出来的代码都是一样的，但不排的话，语义层面则是不正确的。为了简单演示，这里就直接限制要求类型相同而不是判断继承关系&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;能够进入到这一步的，才开始创建列表用于作为返回值&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;result&lt;/span&gt; &lt;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;ItemGeneratedCodeResult&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;div class=&quot;language-csharp highlighter-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;result&lt;/span&gt; &lt;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;ItemGeneratedCodeResult&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;candidateConstructorMethodSymbol&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;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;fullNameDisplayFormat&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SymbolDisplayFormat&lt;/span&gt;
    &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;SymbolDisplayGlobalNamespaceStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Included&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 命名空间之前加上 global 防止冲突&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SymbolDisplayTypeQualificationStyle&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NameAndContainingTypesAndNamespaces&lt;/span&gt;
    &lt;span class=&quot;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;assemblyClassTypeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToDisplayString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fullNameDisplayFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// context =&amp;gt; new F1(context)&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generatedCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;context =&amp;gt; new &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;s&quot;&gt;(context)&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;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;ItemGeneratedCodeResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;generatedCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exportMethodInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratedCodeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span 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;candidateConstructorMethodSymbol&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;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;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;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;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;Add&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;ItemGeneratedCodeResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;with&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;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;最后，将 result 列表返回即可&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&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;nf&quot;&gt;ToImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&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;ItemGeneratedCodeResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;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;以上代码将 ` List&lt;ItemGeneratedCodeResult&gt;` 转换为不可变的数组进行返回，如此可以更好的符合分析器的设计。整个寻找整个项目的感兴趣的类型和生成部分代码的过程的代码如下&lt;/ItemGeneratedCodeResult&gt;&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;IncrementalValuesProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&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;ItemGeneratedCodeResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;itemGeneratedCodeResultProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;wholeAssemblyClassTypeProvider&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;collectionMethodInfoProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&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;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INamedTypeSymbol&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;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CollectionExportMethodInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&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;n&quot;&gt;tuple&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;token&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;INamedTypeSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assemblyClassTypeSymbol&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tuple&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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exportMethodReturnTypeCollectionResultArray&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tuple&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;c1&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;n&quot;&gt;ItemGeneratedCodeResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;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;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CollectionExportMethodInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exportMethodInfo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exportMethodReturnTypeCollectionResultArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;// 一般进入循环的时候，都会加上这个判断。这个判断逻辑的作用是如开发者在 IDE 里面进行编辑文件的时候，那此文件对应的类型就需要重新处理，即类型对应的 token 将会被激活。此时在循环跑的逻辑就是浪费的，逻辑需要重跑，因此需要判断 token 是否被取消，减少循环里面的不必要的逻辑损耗&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;// check for cancellation so we don&apos;t hang the host&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;ThrowIfCancellationRequested&lt;/span&gt;&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;IsInherit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assemblyClassTypeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exportMethodInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CollectionType&lt;/span&gt;&lt;span class=&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;c1&quot;&gt;// 遍历其构造函数，找到感兴趣的构造函数&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;IMethodSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;candidateConstructorMethodSymbol&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;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IMethodSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;constructorMethodSymbol&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assemblyClassTypeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Constructors&lt;/span&gt;&lt;span class=&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;constructorMethodSymbol&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;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;c1&quot;&gt;// 判断参数的类型是否符合预期&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;IParameterSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameterSymbol&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;constructorMethodSymbol&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;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;parameterType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameterSymbol&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;SymbolEqualityComparer&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;nf&quot;&gt;Equals&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;exportMethodInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConstructorArgumentType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;candidateConstructorMethodSymbol&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;constructorMethodSymbol&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;n&quot;&gt;result&lt;/span&gt; &lt;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;ItemGeneratedCodeResult&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;fullNameDisplayFormat&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SymbolDisplayFormat&lt;/span&gt;
                        &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;SymbolDisplayGlobalNamespaceStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Included&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                            &lt;span class=&quot;c1&quot;&gt;// 命名空间之前加上 global 防止冲突&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;SymbolDisplayTypeQualificationStyle&lt;/span&gt;
                                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NameAndContainingTypesAndNamespaces&lt;/span&gt;
                        &lt;span class=&quot;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;assemblyClassTypeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToDisplayString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fullNameDisplayFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;candidateConstructorMethodSymbol&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;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;// context =&amp;gt; new F1(context)&lt;/span&gt;
                            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generatedCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;context =&amp;gt; new &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;s&quot;&gt;(context)&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;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;ItemGeneratedCodeResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;generatedCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exportMethodInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratedCodeInfo&lt;/span&gt;&lt;span class=&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;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;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;Add&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;ItemGeneratedCodeResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;with&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;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;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;nf&quot;&gt;ToImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&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;ItemGeneratedCodeResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;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;language-plaintext highlighter-rouge&quot;&gt;itemGeneratedCodeResultProvider&lt;/code&gt; 叠加一个 Where 进行过滤，去掉空集，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;IncrementalValuesProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&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;ItemGeneratedCodeResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;itemGeneratedCodeResultProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;wholeAssemblyClassTypeProvider&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;collectionMethodInfoProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&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;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INamedTypeSymbol&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;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CollectionExportMethodInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&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;n&quot;&gt;tuple&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;token&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;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;Where&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;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&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;ItemGeneratedCodeResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;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;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;itemGeneratedCodeResultProvider&lt;/code&gt; 提供的值都不会是空集&lt;/p&gt;

&lt;p&gt;在这里，大家看到了很多熟悉的类似 Linq 里面的 Where 和 Select 方法。这些方法只是命名上和 Linq 相同，实际上不是原来的 Linq 的方法。但从方法的用途上和设计上，可以看到在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IIncrementalGenerator&lt;/code&gt; 这部分设计里面是非常靠近 Linq 的设计的。在更底层的设计上，所期望的就是让数据可以和 Linq 的数据流设计一样，能够一级级传递，且过程中是 Lazy 的和带缓存的。核心目的就是减少计算压力，充分利用 Roslyn 的不可变性带来的缓存机制，减少分析过程的计算压力，不让原本就很卡的 Visual Studio 更加卡。我将在下文基础知识部分和大家详细解析 Where 和 Select 和 Combine 等这几个基础 IIncrementalGenerator 增量源代码生成器的专有方法&lt;/p&gt;

&lt;p&gt;通过以上的步骤，就完成了收集各个感兴趣类型的构造函数的过程，且生成了对应的创建器委托代码。接下来就可以进行组装最终的代码了&lt;/p&gt;

&lt;p&gt;以上是逐个类型跑出来的，需要将其组装起来，生成最终的代码。这里采用的方法是先用 Collect 将其聚合为一个大数组，再使用 SelectMany 将其散开。为什么需要做合分的处理？原因是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;itemGeneratedCodeResultProvider&lt;/code&gt; 提供的数组是一个类型对应在多个分部方法里面的生成代码，而最终需要生成的是单个分部方法包含多个类型的代码，且期望各个分部方法独立生成。于是就需要先调用 Collect 将其聚合为一个大数组，如此才能让各个分部方法拿到所有感兴趣的类型的生成代码，再调用 SelectMany 方法让每个分部方法独立输出，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;itemGeneratedCodeResultProvider&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;nf&quot;&gt;SelectMany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&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;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ItemGeneratedCodeResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;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;CancellationToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oken&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;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;language-plaintext highlighter-rouge&quot;&gt;itemGeneratedCodeResultProvider&lt;/code&gt; 就是一个多值提供器，提供的每个值都是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImmutableArray&amp;lt;ItemGeneratedCodeResult&amp;gt;&lt;/code&gt; 类型。调用 Collect 之后，就转换成了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImmutableArray&amp;lt;ImmutableArray&amp;lt;ItemGeneratedCodeResult&amp;gt;&amp;gt;&lt;/code&gt; 类型，套了两层数组。在 SelectMany 里面，需要先将其拆散，按照 ItemGeneratedCodeResult 里面的 ExportMethodGeneratedCodeInfo 进行分组。这时候采用 Linq 来写就非常简单，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;itemGeneratedCodeResultProvider&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;nf&quot;&gt;SelectMany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&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;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ItemGeneratedCodeResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;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;CancellationTokentoken&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;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IGrouping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratedCodeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ItemGeneratedCodeResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;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;array&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SelectMany&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;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
         &lt;span class=&quot;c1&quot;&gt;// 如果 Diagnostic 不是空，则证明这条是用来报告的，忽略&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;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;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Diagnostic&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;nf&quot;&gt;GroupBy&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;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExportMethodGeneratedCodeInfo&lt;/span&gt;&lt;span class=&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;language-plaintext highlighter-rouge&quot;&gt;IEnumerable&amp;lt;IGrouping&amp;lt;GeneratedCodeInfo, ItemGeneratedCodeResult&amp;gt;&amp;gt; group&lt;/code&gt; 看似很长，其实含义非常明了，表示的是多个组。每个组的领导就是 GeneratedCodeInfo 类型，实际含义是分部方法。简单来说可以看成 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IEnumerable&amp;lt;分部方法组&amp;gt;&lt;/code&gt; 类型，细分 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;分部方法组&lt;/code&gt; 就包含了分部方法的信息本身，以及各个满足条件的类型和其生成代码。每个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;分部方法组&lt;/code&gt; 就可以组成一个最终生成代码&lt;/p&gt;

&lt;p&gt;将 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IEnumerable&amp;lt;IGrouping&amp;lt;GeneratedCodeInfo, ItemGeneratedCodeResult&amp;gt;&amp;gt; group&lt;/code&gt; 进行遍历，每一项都可生成一个独立的分部方法的实现代码&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IGrouping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratedCodeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ItemGeneratedCodeResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;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;array&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SelectMany&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;n&quot;&gt;t&lt;/span&gt;&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;Where&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;n&quot;&gt;t&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;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;nf&quot;&gt;GroupBy&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;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExportMethodGeneratedCodeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generatedCodeList&lt;/span&gt; &lt;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;GeneratedCodeInfo&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;n&quot;&gt;IGrouping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratedCodeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ItemGeneratedCodeResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;temp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&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;c1&quot;&gt;// 在这里就是可以组装出各个标记了 CollectionAttribute 特性的分部方法的实现代码&lt;/span&gt;
      &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;以下就是组装标记了 CollectionAttribute 特性的分部方法的实现代码，先将各个满足条件的类型的生成代码放入到 StringBuilder 里面，转换为方法体核心内容，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;generatedCodeList&lt;/span&gt; &lt;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;GeneratedCodeInfo&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;n&quot;&gt;IGrouping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratedCodeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ItemGeneratedCodeResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;temp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&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;c1&quot;&gt;// 进行组装生成代码。在 Select 系列方法组装会比在 RegisterSourceOutput 更好，在这里更加便被打断&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&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;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ItemGeneratedCodeResult&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;itemGeneratedCodeResult&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&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;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;ThrowIfCancellationRequested&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//         yield return context =&amp;gt; new F1(context);&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;AppendLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;         yield return itemGeneratedCodeResult.ItemGeneratedCode};&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;// 严谨一些，添加 break 语句。顺带解决收集不到任何一个类型的情况&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;AppendLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;         yield break;&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;在以上生成代码里面，还在最后添加了 ` yield break;` 代码，如此可以顺带解决收集不到任何一个类型的情况，即使收集不到一个类型，也能返回空集，而不会让构建炸掉&lt;/p&gt;

&lt;p&gt;再根据上文提供的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield return context =&amp;gt; new F1(context);&lt;/code&gt; 用于被替换的预置内容，将其进行替换，即可完成分部方法实现方法体的内容&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;replacedCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;        yield return context =&amp;gt; new F1(context);&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;GeneratedCodeInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generatedCodeInfo&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;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;generatedCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generatedCodeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&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;n&quot;&gt;replacedCode&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当前的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;generatedCode&lt;/code&gt; 变量的内容大概如下，即以下代码内容就是最终的分部方法生成的方法体内容示例&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;KawhawnahemCanalllearlerwhu&lt;/span&gt;&lt;span class=&quot;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;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FooCollection&lt;/span&gt;
&lt;span class=&quot;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;partial&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;Collections&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;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&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;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&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;KawhawnahemCanalllearlerwhu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IContext&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;KawhawnahemCanalllearlerwhu&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;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;GetFooCreatorList&lt;/span&gt;&lt;span class=&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;context&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;k&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;KawhawnahemCanalllearlerwhu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;F1&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;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;context&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;k&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;KawhawnahemCanalllearlerwhu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;F2&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;k&quot;&gt;yield&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;生成最终代码之后，将其加入到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;generatedCodeList&lt;/code&gt; 列表里面，如以下代码所示&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;generatedCodeList&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;GeneratedCodeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;generatedCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generatedCodeInfo&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;language-plaintext highlighter-rouge&quot;&gt;generatedCodeList&lt;/code&gt; 列表返回即可&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;IncrementalValuesProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratedCodeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generatedCodeInfoProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;itemGeneratedCodeResultProvider&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;nf&quot;&gt;SelectMany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&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;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ItemGeneratedCodeResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;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;CancellationToken&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;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;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IGrouping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratedCodeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ItemGeneratedCodeResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;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;array&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SelectMany&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;n&quot;&gt;t&lt;/span&gt;&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;Where&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;n&quot;&gt;t&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;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;nf&quot;&gt;GroupBy&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;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExportMethodGeneratedCodeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generatedCodeList&lt;/span&gt; &lt;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;GeneratedCodeInfo&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;n&quot;&gt;IGrouping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratedCodeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ItemGeneratedCodeResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;temp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generatedCode&lt;/span&gt; &lt;span class=&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;generatedCodeList&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;GeneratedCodeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;generatedCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generatedCodeInfo&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;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generatedCodeList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;是否大家好奇为什么 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;generatedCodeInfoProvider&lt;/code&gt; 是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValuesProvider&amp;lt;GeneratedCodeInfo&amp;gt;&lt;/code&gt; 类型？明明最后返回的是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;List&amp;lt;GeneratedCodeInfo&amp;gt; generatedCodeList&lt;/code&gt; 列表？这是因为当前调用的就是 SelectMany 方法，其功能和 Linq 的 SelectMany 方法一样，都是将返回的列表进行拆散&lt;/p&gt;

&lt;p&gt;最后一步就是将生成的代码进行注入，这里调用的是 RegisterImplementationSourceOutput 方法。回顾一下几个注入生成代码的方法的差别，其中 RegisterImplementationSourceOutput 的作用是注册具体的实现部分的生成代码。这个方法的调用可以让 IDE 进行充分的优化，因为具体的实现的内容不影响外部的语法语义分析，外部的语法语义分析靠定义部分即可完成，因此对于实现的内容部分可以尽量慢点调用和减少调用频次。刚好在本演练里面，生成的代码就是具体的实现部分，调用 RegisterImplementationSourceOutput 方法是非常合适的&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterImplementationSourceOutput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;generatedCodeInfoProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SourceProductionContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;productionContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GeneratedCodeInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generatedCodeInfo&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;productionContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddSource&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;generatedCodeInfo&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;.cs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generatedCodeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratedCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;按照源代码生成器的最佳实践来说，在 RegisterImplementationSourceOutput 等注册代码步骤里面，应该接近没有逻辑。即在值提供器步骤时，就将代码生成完成，到 RegisterXxx 方法里面只做注册而已。这样就比较方便在前面步骤进行打断，以及缓存复用&lt;/p&gt;

&lt;h4 id=&quot;报告-diagnostic-信息&quot;&gt;报告 Diagnostic 信息&lt;/h4&gt;

&lt;p&gt;认真阅读文章的伙伴也许还记得上文部分提到了 Diagnostic 信息。在源代码生成器的过程中，如果发现某些代码不符合预期，可以通过报告 Diagnostic 信息，用于提示开发者。在上文的代码里面，如果找不到满足条件的构造函数，就会报告一条分析警告。这里就来详细介绍如何报告 Diagnostic 信息&lt;/p&gt;

&lt;p&gt;回到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;itemGeneratedCodeResultProvider&lt;/code&gt; 变量的赋值部分代码，即使对全项目的类型 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wholeAssemblyClassTypeProvider&lt;/code&gt; 进行“遍历”的时候，如果一个类型满足分部方法收集条件，但不存在任何一个满足条件的构造函数时，应该给出分析警告。如本演练给出的 F3 测试类型的定义如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;F3&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;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;F3&lt;/span&gt;&lt;span class=&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;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FooCollection&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&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;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&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;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IContext&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;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetFooCreatorList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以明显看出给出的 F3 测试类型是歪楼的，没有构造函数满足 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetFooCreatorList&lt;/code&gt; 收集条件。于是在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wholeAssemblyClassTypeProvider&lt;/code&gt; 进行遍历的过程中，应该对这些歪楼的类型给出警告&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;IncrementalValuesProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INamedTypeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wholeAssemblyClassTypeProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;wholeAssemblyClassTypeProvider&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;collectionMethodInfoProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&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;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INamedTypeSymbol&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;ImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CollectionExportMethodInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&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;n&quot;&gt;tuple&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;token&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;INamedTypeSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assemblyClassTypeSymbol&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tuple&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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exportMethodReturnTypeCollectionResultArray&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tuple&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;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ItemGeneratedCodeResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;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;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CollectionExportMethodInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exportMethodInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inexportMethodReturnTypeCollectionResultArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&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;n&quot;&gt;IMethodSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;candidateConstructorMethodSymbol&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;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;candidateConstructorMethodSymbol&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;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;else&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;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;Add&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;ItemGeneratedCodeResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;with&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;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;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;nf&quot;&gt;ToImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&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;ItemGeneratedCodeResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;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;language-plaintext highlighter-rouge&quot;&gt;Diagnostic diagnostic = ...&lt;/code&gt; 这句代码的具体内容。首先需要创建一个 DiagnosticDescriptor 对象，用于描述这个 Diagnostic 信息的基本信息，如 ID、Title、MessageFormat、Category 等。这是因为大部分的 Diagnostic 基本信息都是固定的，唯一不同的就是具体的 Message 提示消息内容的一些参数不同，以及代码 Location 的不同而已。再加上为了让分析器能够提供给全球的开发者使用，自然要照顾多语言问题，这部分就可以独立出来定义，不用每次报告都重复生成。当然了，为了演示方便，我这里就在每次循环里面，每找到一次不满足条件的构造函数就创建一个 DiagnosticDescriptor 对象，实际项目中应该是提前定义好的，不用每次循环都创建，甚至作为静态的字段都是合理的&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;diagnosticDescriptor&lt;/span&gt; &lt;span class=&quot;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;id&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;Resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Kaw001&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;nf&quot;&gt;Localize&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;Resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Kaw001&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;nf&quot;&gt;Localize&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;Resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Kaw001_Message&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;s&quot;&gt;&quot;FooCompiler&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;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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;大家可以发现，以上代码写了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Resources.Kaw001&lt;/code&gt; 资源定义，这部分是依靠在分析器项目里面建立 Resources.resx 资源而被生成的属性。通过建立 Resources.resx 文件，可以复用原本 dotnet 内建的多语言机制，生成多语言程序集等方式提供多语言包。本文这里不过多介绍多语言的创建方式，大家感兴趣还请自行了解&lt;/p&gt;

&lt;p&gt;具体做法就是创建 Resources.resx 文件，确保在 csproj 项目里面里面设置为 ResXFileCodeGenerator 生成方式或&lt;a href=&quot;https://github.com/ycanardeau/ResXGenerator&quot;&gt;其他&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;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;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;DesignTime&amp;gt;&lt;/span&gt;True&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DesignTime&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;AutoGen&amp;gt;&lt;/span&gt;True&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AutoGen&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;DependentUpon&amp;gt;&lt;/span&gt;Resources.resx&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;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;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;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Generator&amp;gt;&lt;/span&gt;ResXFileCodeGenerator&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Generator&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;LastGenOutput&amp;gt;&lt;/span&gt;Resources.Designer.cs&lt;span class=&quot;nt&quot;&gt;&amp;lt;/LastGenOutput&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/EmbeddedResource&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;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门13.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2025317852487163.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在 Resources.resx 文件里面添加两项，内容分别如下&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Kaw001&lt;/code&gt; : 找不到符合预期的构造函数&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Kaw001_Message&lt;/code&gt; : 无法从 {0} 类型中找到构造函数，期望构造函数的只有一个参数，且参数为 {1} 类型&lt;/li&gt;
&lt;/ul&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门14.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F202531785439974.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如上文代码，可见 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Kaw001&lt;/code&gt; 将被当成标题，而 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Kaw001_Message&lt;/code&gt; 被作为具体警告内容。其中 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Kaw001_Message&lt;/code&gt; 添加了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{0}&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{1}&lt;/code&gt; 内容，用于分别替换为具体警告信息内容的具体类型&lt;/p&gt;

&lt;p&gt;为了作为警告内容，在 DiagnosticDescriptor 需要将 DiagnosticSeverity 设置为 Warning 等级。如期望作为错误，则需要设置为 Error 才可以。可用选项如下所示&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;Describes how severe a diagnostic is.&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;DiagnosticSeverity&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;/// Something that is an issue, as determined by some authority,&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// but is not surfaced through normal means.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// There may be different mechanisms that act on these issues.&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;Hidden&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;/// Information that does not indicate a problem (i.e. not prescriptive).&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;Info&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;Something suspicious but allowed.&amp;lt;/summary&amp;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;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// Something not allowed by the rules of the language or other authority.&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;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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;category&lt;/code&gt; 分类是自己分析器内自定义的，这部分没有做要求，只要自己分类好就可以了。在我所在的团队的 &lt;a href=&quot;https://github.com/dotnet-campus/dotnetCampus.MSBuildUtils&quot;&gt;https://github.com/dotnet-campus/dotnetCampus.MSBuildUtils&lt;/a&gt; 开源项目里面就内建了一些常用的分类，大家如果没有思路可以参考&lt;/p&gt;

&lt;p&gt;上文代码中的 DiagnosticDescriptor 构造函数参数的 Localize 方法的实现如下，其作用是返回支持语言文化的 LocalizableString 类型而不是具体字符串&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;LocalizableString&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Localize&lt;/span&gt;&lt;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;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LocalizableResourceString&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;Resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ResourceManager&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;Resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上文代码里面选用的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Kaw001&lt;/code&gt; 也是有约束的，即这是一个 C# 的标识符，使用前缀加数字形式，长度小于 15 个字符，确保唯一性。详细约束请参阅 &lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/choosing-diagnostic-ids&quot;&gt;https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/choosing-diagnostic-ids&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;完成了对 DiagnosticDescriptor 的定义之后，接下来就可以开始创建 Diagnostic 对象。也如 DiagnosticDescriptor 的构造函数可以看到，其实基本信息都全了，剩下的就是填充具体警告信息的参数内容，即 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{0}&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{1}&lt;/code&gt; 参数内容，以及可选的警告的代码 Location 在哪信息而已&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;fullNameDisplayFormat&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SymbolDisplayFormat&lt;/span&gt;
&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;SymbolDisplayGlobalNamespaceStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Included&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 命名空间之前加上 global 防止冲突&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SymbolDisplayTypeQualificationStyle&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NameAndContainingTypesAndNamespaces&lt;/span&gt;
&lt;span class=&quot;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;assemblyClassTypeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToDisplayString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fullNameDisplayFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 无法从 {0} 类型中找到构造函数，期望构造函数的只有一个参数，且参数类型为 {1}&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;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;diagnosticDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exportMethodInfo&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;messageArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&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;exportMethodInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConstructorArgumentType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToDisplayString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fullNameDisplayFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span 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;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;lindexi&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Code&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Roslyn&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;KawhawnahemCanalllearlerwhu&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;KawhawnahemCanalllearlerwhu&lt;/span&gt;&lt;span class=&quot;err&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;cs&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;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;41&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;81&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;n&quot;&gt;Kaw001&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;k&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;KawhawnahemCanalllearlerwhu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;F3&lt;/span&gt; &lt;span class=&quot;err&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;KawhawnahemCanalllearlerwhu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IContext&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;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门18.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20253191036454746.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这个过程中可以看到似乎有分析器的影子在里面了，报告 Diagnostic 过程本身也就是分析器的一个部分，大部分分析器的功能都是和源代码生成器相互重叠的，比如都需要进行语法语义的分析。不同点只是源代码生成器多了一个生成代码的过程&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/dotnet-campus/Telescope&quot;&gt;https://github.com/dotnet-campus/Telescope&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;代码&quot;&gt;代码&lt;/h3&gt;

&lt;p&gt;本章的代码放在 &lt;a href=&quot;https://github.com/lindexi/lindexi_gd/tree/7799af7403b6408b1e30151e144b2273c86433c7/Roslyn/KawhawnahemCanalllearlerwhu&quot;&gt;github&lt;/a&gt; 和 &lt;a href=&quot;https://gitee.com/lindexi/lindexi_gd/tree/7799af7403b6408b1e30151e144b2273c86433c7/Roslyn/KawhawnahemCanalllearlerwhu&quot;&gt;gitee&lt;/a&gt; 上，可以使用如下命令行拉取代码。我整个代码仓库比较庞大，使用以下命令行可以进行部分拉取，拉取速度比较快&lt;/p&gt;

&lt;p&gt;先创建一个空文件夹，接着使用命令行 cd 命令进入此空文件夹，在命令行里面输入以下代码，即可获取到本文的代码&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;git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 7799af7403b6408b1e30151e144b2273c86433c7
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上使用的是国内的 gitee 的源，如果 gitee 不能访问，请替换为 github 的源。请在命令行继续输入以下代码，将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码，可以发邮件向我要代码&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;git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 7799af7403b6408b1e30151e144b2273c86433c7
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;获取代码之后，进入 Roslyn/KawhawnahemCanalllearlerwhu 文件夹，即可获取到源代码&lt;/p&gt;

&lt;h3 id=&quot;生成的源代码保存到本地文件&quot;&gt;生成的源代码保存到本地文件&lt;/h3&gt;

&lt;p&gt;在本演练里面生成的代码还算简单，也不知道大家是否在一开始就在好奇生成的代码是什么样子的。接下来我将告诉大家如何将生成的源代码保存到本地文件&lt;/p&gt;

&lt;p&gt;对于比较复杂的生成代码而言，有时候会导致项目构建不通过。这个技术在实际开发中非常有用，默认的生成代码只能在 VisualStudio 里面的分析器里面一项项展开查看，没有具体的文件路径，对于源代码生成器作者的调试分析不够友好，也不方便将生成的代码发送给其他开发者辅助调试。将生成的源代码保存到本地文件，可以更好的辅助大家进行阅读和调试，以及采用第三方工具辅助分析生成的代码内容&lt;/p&gt;

&lt;p&gt;将生成的源代码保存到本地文件只需在 csproj 项目文件里面设置 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EmitCompilerGeneratedFiles&lt;/code&gt; 属性即可，设置完成之后，默认的生成源代码将会存放到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$(IntermediateOutputPath)\generated&lt;/code&gt; 文件夹里面，这里的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$(IntermediateOutputPath)&lt;/code&gt; 由 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;obj\$(Configuration)\$(TargetFramework.ToLowerInvariant())\&lt;/code&gt; 构成，调试下的输出大概是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;obj\Debug\net9.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;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;EmitCompilerGeneratedFiles&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/EmitCompilerGeneratedFiles&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;如果期望自己指定保存的文件夹，可以自行设置 EmitCompilerGeneratedFiles 属性，如以下代码&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div 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;CompilerGeneratedFilesOutputPath&amp;gt;&lt;/span&gt;Generated\$(TargetFramework)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/CompilerGeneratedFilesOutputPath&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;以上代码之所以拼接上 TargetFramework 是因为期望默认处理多框架的文件冲突问题，源代码生成器会在多框架下分别执行，为每个框架生成独立的代码。如果在多框架项目下没有配置加上 TargetFramework 将会造成生成的源代码存放的文件冲突&lt;/p&gt;

&lt;p&gt;更多请参阅 &lt;a href=&quot;/post/%E5%B0%86-Source-Generator-%E7%94%9F%E6%88%90%E7%9A%84%E6%BA%90%E4%BB%A3%E7%A0%81%E4%BF%9D%E5%AD%98%E5%88%B0%E6%9C%AC%E5%9C%B0%E6%96%87%E4%BB%B6.html&quot;&gt;将 Source Generator 生成的源代码保存到本地文件&lt;/a&gt;
&lt;!-- [将 Source Generator 生成的源代码保存到本地文件 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18011557 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;基础知识&quot;&gt;基础知识&lt;/h3&gt;

&lt;p&gt;在上文介绍了基础 IIncrementalGenerator 增量源代码生成器的专有方法，如 Where 和 Select 和 Combine 等，这里将详细介绍这几个方法的用法和设计&lt;/p&gt;

&lt;p&gt;开始之前重新介绍两个类型的值提供器，分别是 IncrementalValuesProvider 多值提供器和 IncrementalValueProvider 单值提供器。这两个类型的值提供器是源代码生成器的核心，用于提供源代码生成器的输入源&lt;/p&gt;

&lt;p&gt;对于 IncrementalValuesProvider 多值提供器来说，里面的每一项都是独立的。比如以下代码&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;IncrementalValuesProvider&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;n&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&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;provider&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当某个 Foo 项变更的时候，那么 Select 方法里面的代码就会重新执行，且只执行一次。其他没有变更的 Foo 项则不会触发 Select 方法里面的委托执行。通过类似的方式可以应用缓存，减少计算工作量&lt;/p&gt;

&lt;p&gt;有些伙伴会感觉增量源代码生成器这部分 API 比较复杂。确实是比较复杂。但大家需要明确的是，咱现在正在编写的是和编译器相关的代码，所有和编译器沾边的，其难度都不低。在 Roslyn 以及其周边的设施的设计上，都追求性能、编写的复杂度、可维护性等多方面的平衡。如果没有源代码生成的 API 封装，直接面对最裸的编译器相关实现逻辑，那其开发难度和入门门槛可想而知的高&lt;/p&gt;

&lt;p&gt;在性能追求方面上，性能优化常用套路里面就是减少计算量。除了语言层面能够提升之外，减少计算工作量能达到算法级的优化，这才是真正的优化。尽管 Roslyn 在发布之初就强调了性能，但即使单次构建足够快，架不住次数多。比如一个代码文件压到 1 毫秒，但我的项目有 2000 个文件，我假设无时不刻都在修改代码，那么每次构建就是 2000 毫秒，也就是 2 秒。这个时间对于一个大型项目来说，还是比较可观的。但事实上，绝大部分的代码我都没有动到，只有少部分的代码在修改。这时候就需要增量构建，只处理修改的代码，这样就能大大减少构建时间。这就是增量构建的优势所在&lt;/p&gt;

&lt;p&gt;为了达成增量构建，就需要引入缓存不可变机制。引入缓存不可变机制，在一定程度上能够降低整体逻辑复杂度，不需要让程序猿去内耗对象是否被变更等问题。也方便底层设施搭建者进行复现问题，即方便重现问题，各个部件都是不可变的，方法都是无副作用的，自然重现步骤就简单了&lt;/p&gt;

&lt;p&gt;在多方平衡之下，就有了现在大家所看到的 IIncrementalGenerator 增量源代码生成器的各个方法了。虽然看起来复杂，但只要想想原本的开发难度和复杂度，能够被降低到这个程度，就不会觉得这个 API 复杂了&lt;/p&gt;

&lt;p&gt;对于 IncrementalValueProvider 单值提供器来说，里面只提供一个值，有时候这个值是一个数组集合，有时候里面就真的是一个值，比如下文会和大家介绍到的配置内容。在 IIncrementalGenerator 增量源代码生成器里面就充满了聚合和散开的逻辑，也推荐这么干，这样的逻辑更底层的思想是实现细颗粒度管控，能够更好地利用缓存，减少计算工作量&lt;/p&gt;

&lt;p&gt;无论是 IncrementalValuesProvider 多值提供器还是 IncrementalValueProvider 单值提供器，整体设计都是采用管线方式，走数据流的方式，让数据一步步往下走。在每一步的输出里面都进行缓存检查，如果命中缓存，即没有更改，则不会触发后续的计算。这也就是 IIncrementalGenerator 增量源代码生成器命名的由来，即增量构建，只处理变更的部分。而 Linq 刚好就是数据流的一个现有实践，在增量源代码生成器里面复用了这部分的设计思想，只是 API 实现和行为略微不同，接下来我将逐一和大家介绍这几个方法的用法和设计&lt;/p&gt;

&lt;p&gt;本章以下的介绍顺序保持和 &lt;a href=&quot;https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.md&quot;&gt;https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.md&lt;/a&gt; 官方文档相同的顺序，对应的代码附图来源于此官方文档&lt;/p&gt;

&lt;h4 id=&quot;select&quot;&gt;Select&lt;/h4&gt;

&lt;p&gt;方法签名：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IncrementalValueSourceExtensions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 1 =&amp;gt; 1 transform &lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 1 对 1 的转换，从 TSource 转换为 TResult 类型的输出&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 为了简单表述，我使用 `IncrementalValue[s]Provider` 代表 `IncrementalValuesProvider` 和 `IncrementalValueProvider` 两个类型的值提供器都适用的情况&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;IncrementalValue&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;Provider&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;Select&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;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;IncrementalValue&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;Provider&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;n&quot;&gt;CancellationToken&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;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这是一个最常用的转换逻辑，用于从当前提供的数据转换为新的数据进行输出，可同时在 IncrementalValuesProvider 多值提供器和 IncrementalValueProvider 单值提供器&lt;/p&gt;

&lt;p&gt;为了简单表述，我使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValue[s]Provider&lt;/code&gt; 代表 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValuesProvider&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValueProvider&lt;/code&gt; 两个类型的值提供器都适用的情况&lt;/p&gt;

&lt;p&gt;如以下代码所示，从 FooInfo1 数据转换为 FooInfo2 数据&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;IncrementalValuesProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FooInfo1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo1ValuesProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;IncrementalValuesProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FooInfo2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo2ValuesProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo1ValuesProvider&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;FooInfo1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info1&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;token&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;FooInfo2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;转换过程中，支持分叉和链式转换。分叉转换，即从一个 IncrementalValue[s]Provider 分叉为多条不同的转换分支，如下面代码所示&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;IncrementalValuesProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FooInfo1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo1ValuesProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;IncrementalValuesProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FooInfo2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo2ValuesProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo1ValuesProvider&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;FooInfo1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info1&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;token&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;FooInfo2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;IncrementalValuesProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FooInfo3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo3ValuesProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo1ValuesProvider&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;FooInfo1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info1&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;token&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;FooInfo3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可见 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo2ValuesProvider&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo3ValuesProvider&lt;/code&gt; 来源于共同的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo1ValuesProvider&lt;/code&gt; 数据源。这在多个不同的业务逻辑存在共有转换时非常有用，可以更多程度地进行复用计算&lt;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门19.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2025319201467353.jpg&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;n&quot;&gt;IncrementalValuesProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FooInfo1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo1ValuesProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;IncrementalValuesProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FooInfo2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo2ValuesProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo1ValuesProvider&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;FooInfo1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info1&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;token&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;FooInfo2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;IncrementalValuesProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FooInfo3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo3ValuesProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo2ValuesProvider&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;FooInfo2&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info2&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;token&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;FooInfo3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门20.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2025319201704335.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;select-many&quot;&gt;Select Many&lt;/h4&gt;

&lt;p&gt;方法签名：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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 转 多&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;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IncrementalValuesProvider&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;SelectMany&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;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;IncrementalValue&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;Provider&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;n&quot;&gt;CancellationToken&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;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如以下代码所示，先从 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValueProvider&amp;lt;FooInfo1&amp;gt;&lt;/code&gt; 单值提供器，调用 SelectMany 进行单转多，获取到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValuesProvider&amp;lt;FooInfo2&amp;gt;&lt;/code&gt; 多值提供器。再继续对 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValuesProvider&amp;lt;FooInfo2&amp;gt;&lt;/code&gt; 多值提供器调用 SelectMany 进行多转多获取到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValuesProvider&amp;lt;FooInfo3&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;n&quot;&gt;IncrementalValueProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FooInfo1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo1ValueProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;IncrementalValuesProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FooInfo2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo2ValuesProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo1ValueProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SelectMany&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FooInfo1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info1&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;token&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;n&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Number&lt;/span&gt;&lt;span class=&quot;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;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;FooInfo2&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;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;n&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;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;nf&quot;&gt;FooInfo2&lt;/span&gt;&lt;span class=&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;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;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;IncrementalValuesProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FooInfo3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo3ValuesProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo2ValuesProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SelectMany&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FooInfo2&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info2&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;token&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;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;FooInfo3&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;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;info2&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;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;nf&quot;&gt;FooInfo3&lt;/span&gt;&lt;span class=&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;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;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;假定 FooInfo1 的 Number 的是 3 的值。每个 FooInfo2 的 Count 也是 3 的值，则经过以上转换之后，可获取带有 3x3=9 个元素的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValuesProvider&amp;lt;FooInfo3&amp;gt;&lt;/code&gt; 多值提供器&lt;/p&gt;

&lt;p&gt;从 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValueProvider&amp;lt;FooInfo1&amp;gt;&lt;/code&gt; 单值提供器，调用 SelectMany 进行单转多，获取到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValuesProvider&amp;lt;FooInfo2&amp;gt;&lt;/code&gt; 多值提供器的过程是 1 转多的过程，相对来说很是清晰&lt;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门21.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20253192022298551.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;对 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValuesProvider&amp;lt;FooInfo2&amp;gt;&lt;/code&gt; 多值提供器调用 SelectMany 进行多转多获取到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValuesProvider&amp;lt;FooInfo3&amp;gt;&lt;/code&gt; 多值提供器的过程，相对来说就比较复杂，如以下的官方附图，每项都可转换为不定数量集合输出&lt;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门22.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20253192028418785.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;敲黑板，使用 SelectMany 的过程中，也可以附带过滤的作用。如上文所述，每项都可转换为不定数量集合输出，不定数量就意味着也可以返回 0 项。在 SelectMany 执行过滤作用的做法就是将不满足条件的直接过滤掉，甚至返回空集合。因此比较少见 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SelectMany(...).Where(...)&lt;/code&gt; 的组合，直接就是在 SelectMany 里面内置了 Where 的活了&lt;/p&gt;

&lt;p&gt;常见于将 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SelectMany&lt;/code&gt; 和下文介绍的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Collect&lt;/code&gt; 混用，达成合和分的效果&lt;/p&gt;

&lt;h4 id=&quot;where&quot;&gt;Where&lt;/h4&gt;

&lt;p&gt;方法签名：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;IncrementalValuesProvider&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;Where&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;IncrementalValuesProvider&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;没错，只有多值提供器才有 Where 方法。通过 Where 方法可用来过滤输入源里面符合条件的元素，将符合条件的元素作为输出源内容&lt;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门23.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20253192033175447.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如上图官方附图所示，假定输入源有三个，中间一个不满足条件，也就是上面打了叉叉的 Item2 项，则最终只有 Item1 和 Item3 才能流向输出源里&lt;/p&gt;

&lt;p&gt;正如大家所熟悉的 Linq 里面的 Select 和 Where 配合一样，在增量源代码生成器这里对这两个的用法和设计实现也都和 Linq 的相同&lt;/p&gt;

&lt;h4 id=&quot;collect&quot;&gt;Collect&lt;/h4&gt;

&lt;p&gt;方法签名：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;IncrementalValueProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&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;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Collect&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;IncrementalValuesProvider&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这是一个不用附带任何条件和转换器的方法。用于将一个多值提供器的内容，转换为单值提供器。这个过程中，一旦输入源有任何一项变动，则会重新输出整个新的不可变集合&lt;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门24.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20253192037509811.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如以上官方附图所示，通过 Collect 方法将一个多值提供器转换为一个单值提供器，且这个单值提供器提供的单个值就是一个集合&lt;/p&gt;

&lt;p&gt;这个 Collect 过程可以认为和 SelectMany 是互逆的过程，即可以从 Collect 由多值提供器转换为一个单值提供器，再从 SelectMany 由单值提供器转换为多值提供器。如以下代码所示&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;IncrementalValuesProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FooInfo1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo1ValuesProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...;&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;IncrementalValueProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&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;FooInfo1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo1ArrayValueProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo1ValuesProvider&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;IncrementalValuesProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FooInfo1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;backToValuesProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo1ArrayValueProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SelectMany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&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;FooInfo1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;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;CancellationToken&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;p&quot;&gt;=&amp;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;foo1ValuesProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;backToValuesProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上代码先使用 Collect 方法，从 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValuesProvider&amp;lt;FooInfo1&amp;gt;&lt;/code&gt; 多值提供器，转换为带不可变集合的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValueProvider&amp;lt;ImmutableArray&amp;lt;FooInfo1&amp;gt;&amp;gt;&lt;/code&gt; 单值提供器&lt;/p&gt;

&lt;p&gt;再调用 SelectMany 方法，重新将 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValueProvider&amp;lt;ImmutableArray&amp;lt;FooInfo1&amp;gt;&amp;gt;&lt;/code&gt; 单值提供器转换为原来的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValuesProvider&amp;lt;FooInfo1&amp;gt;&lt;/code&gt; 多值提供器。从以上代码最后一行可以看到，经过 SelectMany 转换回来的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backToValuesProvider&lt;/code&gt; 的类型是完全和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo1ValuesProvider&lt;/code&gt; 一样的，相互赋值都能通过构建&lt;/p&gt;

&lt;h4 id=&quot;split&quot;&gt;Split&lt;/h4&gt;

&lt;p&gt;准确来说这只是一个用法，不是一个 API 方法。表示的就是分叉调用，多分支调用。如在 Select 一节中和大家介绍，允许进行分叉转换。事实上，在以上介绍的每个内容里面，每个值提供器，无论是多值提供器还是单值提供器，都可以被多次调用各个方法作为输入源进行消费。这和 Linq 里面的固有印象有所不同，在 Linq 里面，枚举 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IEnumerable&amp;lt;TSource&amp;gt;&lt;/code&gt; 是不支持多次重复消费的，多次消费将获取不可控结果。但在源代码生成器这里面，数据源的提供依靠的是缓存失效来驱动，或者称为数据变更驱动。一旦有数据变更，缓存失效，则会一条链路进行传递&lt;/p&gt;

&lt;p&gt;咱所编写的对各个值提供器的各种转换逻辑，只是用于写入记录转换链路而已。当数据变更的时候，将会重新开始跑整个链路。在跑的过程中，引入了大量缓存判定，从而最大程度减少执行逻辑量&lt;/p&gt;

&lt;p&gt;在演练中，咱也用到了 Split 的功能，即在拿到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValuesProvider&amp;lt;ImmutableArray&amp;lt;ItemGeneratedCodeResult&amp;gt;&amp;gt; itemGeneratedCodeResultProvider&lt;/code&gt; 数据源时，一路作为 Diagnostic 报告输出，一路作为最终源代码生成的输出&lt;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门27.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20253192117417689.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;combine&quot;&gt;Combine&lt;/h4&gt;

&lt;p&gt;方法签名：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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 对 1 合并&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IncrementalValueProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TLeft&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;TRight&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Combine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TRight&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;IncrementalValueProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IncrementalValueProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TRight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider2&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;n&quot;&gt;IncrementalValuesProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TLeft&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;TRight&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Combine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TRight&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;IncrementalValuesProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IncrementalValueProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TRight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;和以上的分叉相对，以上的 Split 分叉是将一条值提供器作为多个数据提供源，将一个数据链路拆分为多个数据链路。而 Combine 则是将两个数据链路合并到一个链路。能够支持的合并方式是两个单值提供器的合并，以及一个多值提供器和一个单值提供器的合并&lt;/p&gt;

&lt;p&gt;两个单值提供器的合并：&lt;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门26.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20253192059107109.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如以上的官方附图，将两个单值提供器的合并，返回结果依然是一个单值提供器。只是返回的输出源里面包含的是一个元组，其中左右值就是所 Combine 顺序的左右值。如以下代码所示&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;IncrementalValueProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FooInfo1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo1ValueProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;IncrementalValueProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FooInfo2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo2ValueProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;IncrementalValueProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FooInfo1&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;FooInfo2&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo1AndFoo2CombineValueProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo1ValueProvider&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;foo2ValueProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上代码分别将 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValueProvider&amp;lt;FooInfo1&amp;gt;&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValueProvider&amp;lt;FooInfo2&amp;gt;&lt;/code&gt; 两个单值提供器进行合并。合并之后获得了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncrementalValueProvider&amp;lt;(FooInfo1 Left, FooInfo2 Right)&amp;gt;&lt;/code&gt; 的单值提供器&lt;/p&gt;

&lt;p&gt;一个多值提供器和一个单值提供器的合并：&lt;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门25.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20253192058297227.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如以上的官方附图，最终输出源里面是多值提供器里面的每一项都带着单值提供器里面的内容。即输出源里面的元组的左侧是多值提供器里面的每一项，右侧都是相同的单值提供器里面的元素&lt;/p&gt;

&lt;p&gt;那两个多值提供器的合并呢？&lt;/p&gt;

&lt;p&gt;敲黑板，在 Combine 方法里面不支持两个多值提供器的合并。因为一旦两个多值提供器进行合并，则一旦出现任何一方某个元素的缓存失效问题，将会有笛卡尔积次的执行风险。只提供一个多值提供器和一个单值提供器的合并，则可以明确让源代码生成器开发者决定其优化方向，即将哪方作为单值提供器&lt;/p&gt;

&lt;p&gt;那假定我的业务上就是有两个多值提供器，我确实下一步的逻辑就需要两个多值提供器提供的数据才能完成工作。那此时应该如何开展呢？相信会灵活运用所学知识的伙伴已经想到了方法了。没错，就是将其中一个多值提供器调用 Collect 方法，将其转换为单值提供器，于是就可以继续愉快地调用 Combine 进行一个多值提供器和一个单值提供器的合并。这个过程中，源代码生成器开发者可选用两个多值提供器中量小、变化次数少的一方调用 Collect 转换为单值提供器，从而提供更多的优化效果&lt;/p&gt;

&lt;p&gt;以上就是增量源代码生成器的专有基础知识，合理运用好以上的几个数据源处理方法，即可实现对复杂的数据处理的同时，减少计算量&lt;/p&gt;

&lt;h2 id=&quot;演练源代码专有-interceptor-技术&quot;&gt;演练：源代码专有 Interceptor 技术&lt;/h2&gt;

&lt;p&gt;经过了上文的介绍，大家是否对源代码生成器的基础知识有了一定的了解。是否会存在一个错觉，认为源代码生成器最多只是减轻人类程序猿的工作量，但并不能轻易突破人类程序猿难以做到的事情？接下来我将介绍源代码生成器的专有技术 Interceptor 拦截器技术，这是一种源代码生成器专有的技术，可以在构建过程中执行额外的逻辑实现拦截现有代码的功能&lt;/p&gt;

&lt;p&gt;或许换个叫法大家会更熟悉这一类型的技术，即 AOP 面向切面编程。核心差别在于源代码生成器方式的 AOP 是发生在编译阶段，可以做到零反射。且过程中是可以实现到完全的调用转发。即大家进行静态代码阅读的时候，看到的是调用了 A 方法，然而实际构建出来的代码是调用了 B 方法。这种技术在实际开发中非常有用，比如在构建过程中进行日志记录、性能监控、权限控制等等&lt;/p&gt;

&lt;p&gt;现在直接使用 Interceptor 技术的就有 ASP.NET Core 的配置和部分日志的等模块功能，详细请参阅 &lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/core/extensions/configuration-generator&quot;&gt;Compile-time configuration source generation - .NET - Microsoft Learn&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;在本演练中，我将和大家介绍如何在源代码生成器里面使用 Interceptor 技术，实现对现有代码的拦截调用转发。将原本调用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 类型的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;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;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;c&lt;/span&gt; &lt;span class=&quot;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;c&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;m&quot;&gt;1&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;nf&quot;&gt;WriteLine&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;c&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;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;p&quot;&gt;}&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;WriteLine&lt;/span&gt;&lt;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;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;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: &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;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;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;m&quot;&gt;1&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;m&quot;&gt;2&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;m&quot;&gt;3&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;language-plaintext highlighter-rouge&quot;&gt;JuqawhicaqarLairciwholeni&lt;/code&gt; 的控制台项目，和名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JuqawhicaqarLairciwholeni.Analyzer&lt;/code&gt; 的分析器项目。项目搭建方式和上文介绍的一样，不再赘述&lt;/p&gt;

&lt;p&gt;在本演练任务里面，咱需要拦截所有对 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 类型的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WriteLine&lt;/code&gt; 方法的调用，将其转换为源代码生成器所生成的新的代码&lt;/p&gt;

&lt;p&gt;开始之前先介绍一下 &lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-12&quot;&gt;C# 12&lt;/a&gt; 引入的 Interceptor 拦截器技术，拦截器技术的核心是通过一个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InterceptsLocationAttribute&lt;/code&gt; 特性标记一个方法。被标记的方法可以拦截在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InterceptsLocationAttribute&lt;/code&gt; 特性上所设置的所要拦截的代码的调用。如以下代码所示，在名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FooInterceptor&lt;/code&gt; 类型的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InterceptorMethod1&lt;/code&gt; 方法上，标记了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InterceptsLocationAttribute&lt;/code&gt; 特性，注明了拦截 &lt;code class=&quot;language-plaintext 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;static&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;FooInterceptor&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// C:\lindexi\Code\JuqawhicaqarLairciwholeni\JuqawhicaqarLairciwholeni\Program.cs(8,11)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InterceptsLocation&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;1&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;s&quot;&gt;&quot;PSnZx2mpBdT444AVZJmMJX8AAABQcm9ncmFtLmNz&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;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InterceptorMethod1&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;k&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JuqawhicaqarLairciwholeni&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;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;param&lt;/span&gt;&lt;span class=&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;Interceptor1: lindexi is doubi&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;language-plaintext highlighter-rouge&quot;&gt;InterceptsLocationAttribute&lt;/code&gt; 特性上标记了人类难懂的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PSnZx2mpBdT444AVZJmMJX8AAABQcm9ncmFtLmNz&lt;/code&gt; 字符串内容。这其实是对 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C:\lindexi\Code\JuqawhicaqarLairciwholeni\JuqawhicaqarLairciwholeni\Program.cs(8,11)&lt;/code&gt; 的一个标识，这个标识是由源代码生成器里面的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SemanticModel&lt;/code&gt; 的 &lt;a href=&quot;https://github.com/dotnet/roslyn/issues/72133&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetInterceptableLocation&lt;/code&gt;&lt;/a&gt; 方法提供的&lt;/p&gt;

&lt;p&gt;假定 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C:\lindexi\Code\JuqawhicaqarLairciwholeni\JuqawhicaqarLairciwholeni\Program.cs(8,11)&lt;/code&gt; 对应的代码就是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;c.WriteLine(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;Interceptor1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lindexi&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doubi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而不是原本预期的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Foo: 1&lt;/code&gt; 的输出内容。这就是 Interceptor 拦截器技术的核心，通过拦截器技术，可以在构建过程中执行额外的逻辑实现拦截现有代码的功能&lt;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门29.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20253202028155776.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;简单了解了 Interceptor 拦截器技术的核心，在本演练中将开始和大家介绍如何实现这个功能，将原本调用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 类型的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WriteLine&lt;/code&gt; 方法，转发到调用源代码新生成的代码里面。以下是我的实现效果，将原本代码里面对 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 类型的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WriteLine&lt;/code&gt; 方法的三个调用，分别转发到源代码生成器所生成的三个不同的方法里面，如下图所示&lt;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门28.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20253202012517222.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;通过本演练的源代码生成器所处理之后，通过 ILSpy 工具查看生成的 dll 文件，可见最终的生成代码是调用了 FooInterceptor 的三个生成的方法，完全不是静态代码所见的调用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 类型的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WriteLine&lt;/code&gt; 方法&lt;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门30.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20253202032373826.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;即在这个过程里面，所发生的所有科技都在构建之中完成，不会在运行时发生任何额外的调用&lt;/p&gt;

&lt;p&gt;在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JuqawhicaqarLairciwholeni.Analyzer&lt;/code&gt; 项目里面新建名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FooIncrementalGenerator&lt;/code&gt; 的继承 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IIncrementalGenerator&lt;/code&gt; 的类型，其代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;Generator&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;FooIncrementalGenerator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IIncrementalGenerator&lt;/span&gt;
&lt;span class=&quot;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;IncrementalGeneratorInitializationContext&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;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;为了分析所有对 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Foo&lt;/code&gt; 类型的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WriteLine&lt;/code&gt; 方法的调用，需要找到所有 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InvocationExpressionSyntax&lt;/code&gt; 类型的语法点。这些语法点就是各个代码里面调用方法等的地方了，再进一步判断调用的是不是名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WriteLine&lt;/code&gt; 的方法，语法判断部分的工作就完成了。也许有伙伴读到这里会有疑问，为什么只是判断调用的是不是名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WriteLine&lt;/code&gt; 的方法，而不再继续判断是不是 Foo 类型的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;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;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Generator&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;FooIncrementalGenerator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IIncrementalGenerator&lt;/span&gt;
&lt;span class=&quot;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;IncrementalGeneratorInitializationContext&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;n&quot;&gt;SyntaxProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateSyntaxProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&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;_&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;node&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InvocationExpressionSyntax&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;invocationExpressionSyntax&lt;/span&gt;&lt;span class=&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;invocationExpressionSyntax&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;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MemberAccessExpressionSyntax&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;memberAccessExpressionSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;// 这是一个调用名为 WriteLine 的方法代码，但就不知道具体是谁的 WriteLine 了。语法过程中是无法知道具体的类型是哪个的&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;// 比如 Foo a = ...; a.WriteLine(...);&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;// 或 Foo b = ...; b.WriteLine(...);&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;// 此时最多在语法层面只判断出是 WriteLine 方法，进一步判断就交给语义过程了&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;memberAccessExpressionSyntax&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;Identifier&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;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;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;n&quot;&gt;syntaxContext&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;)&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;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在语义转换里面，进一步判断所调用的是不是 Foo 类型的 WriteLine 方法，即判断当前调用的 WriteLine 方法是不是在 Foo 类型上面定义的。这里使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SemanticModel&lt;/code&gt; 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetSymbolInfo&lt;/code&gt; 方法获取到调用的方法的符号信息，接着判断方法符号所在的类型是不是 Foo 类型&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyntaxProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateSyntaxProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&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;_&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;span class=&quot;n&quot;&gt;syntaxContext&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;)&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;symbolInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SemanticModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetSymbolInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;syntaxContext&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;symbolInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Symbol&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;IMethodSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methodSymbol&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// 以下这句判断纯属多余，因为语法过程中已经判断了是 WriteLine 方法&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methodSymbol&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;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;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;p&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;c1&quot;&gt;// 语义过程继续判断具体是否 Foo 类型的 WriteLine 方法&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;methodSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContainingType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToDisplayString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SymbolDisplayFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullyQualifiedFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&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;s&quot;&gt;&quot;global::JuqawhicaqarLairciwholeni.Foo&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;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;p&quot;&gt;...&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上代码判断逻辑里，判断是否为 Foo 类型采用的是字符串判断。除了此实现方式外，还可以使用明确的符号判断方法。通常来说，使用字符串判断能够满足绝大部分情况，且能够适应一定程度的模糊处理，符号判断能够实现更加精准。可根据自身业务需求或喜好选用判断方式。在本文末尾常用方法处，我给出了通过符号进行判断的方法，如感兴趣，还请翻到后文了解实现细节&lt;/p&gt;

&lt;p&gt;为了调用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SemanticModel&lt;/code&gt; 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetInterceptableLocation&lt;/code&gt; 方法获取传入到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InterceptsLocationAttribute&lt;/code&gt; 特性的必要参数，这里通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;syntaxContext.Node&lt;/code&gt; 属性拿到当前正在发起方法调用的 InvocationExpressionSyntax 语法节点。接着调用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetInterceptableLocation&lt;/code&gt; 方法获取到拦截器的位置信息&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;invocationExpressionSyntax&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvocationExpressionSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxContext&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;p&quot;&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SemanticModel&lt;/code&gt; 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetInterceptableLocation&lt;/code&gt; 方法还被标记了实验性，调用此方法之前需要使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#pragma warning disable RSEXPERIMENTAL002&lt;/code&gt; 开启实验性功能&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;invocationExpressionSyntax&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvocationExpressionSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxContext&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;cp&quot;&gt;#pragma warning disable RSEXPERIMENTAL002 // 实验性警告，忽略即可
&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;InterceptableLocation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interceptableLocation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SemanticModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetInterceptableLocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;invocationExpressionSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)!;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;此时拿到的 InterceptableLocation 对象就是包含了将要拦截的代码的具体信息，包括具体是哪个文件的哪行哪列代码，且这个过程里面还包含了代码文件的摘要信息，确保将要拦截替换的代码是符合源代码生成器在生成过程中所预期的。如以下代码尝试拿到 DisplayLocation 字符串信息内容&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;displayLocation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interceptableLocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DisplayLocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;displayLocation&lt;/code&gt; 字符串的大概内容是对应的代码的路径和所在行列信息，如以下代码所示&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;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;lindexi&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Code&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JuqawhicaqarLairciwholeni&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JuqawhicaqarLairciwholeni&lt;/span&gt;&lt;span class=&quot;err&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;cs&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;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;为了演示效果，我这里还尝试使用语法语义方式读取在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Program.cs&lt;/code&gt; 里面调用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WriteLine&lt;/code&gt; 方法的参数值，即调用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WriteLine&lt;/code&gt; 方法的参数值是多少。这里使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;invocationExpressionSyntax.ArgumentList.Arguments&lt;/code&gt; 属性获取到所有的参数列表，再取其首个参数。最后配合语义获取传入的常量值&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;ArgumentSyntax&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argumentSyntax&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;invocationExpressionSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ArgumentList&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;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;argument&lt;/span&gt; &lt;span class=&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;syntaxContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SemanticModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetConstantValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argumentSyntax&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;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;/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;generatedCode&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;&quot;&quot;
&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;Foo_JuqawhicaqarLairciwholeni&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;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FooInterceptor&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;c1&quot;&gt;// ｛｛displayLocation｝｝&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InterceptsLocation&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;err&quot;&gt;｛｛&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;interceptableLocation&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;err&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;s&quot;&gt;&quot;｛｛interceptableLocation.Data｝｝&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;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InterceptorMethod&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;｛｛&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argument&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;this&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;｛｛&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;err&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;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;param&lt;/span&gt;&lt;span class=&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;Interceptor｛｛argument｝｝: lindexi is doubi&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;k&quot;&gt;namespace&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;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;true&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;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;InterceptsLocationAttribute&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;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InterceptsLocationAttribute&lt;/span&gt;&lt;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;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;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;_&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;_&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;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;&quot;;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门31.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20253202056566003.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如以上代码所示，可以看到将 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;displayLocation&lt;/code&gt; 作为注释放在拦截方法上方，如此即可在阅读源代码生成器所生成的代码的时候，可以看到被拦截的代码的位置信息。接着再将读取到的传入参数信息拼接成为拦截方法的方法名以及作为拦截方法的输出内容&lt;/p&gt;

&lt;p&gt;由于 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InterceptsLocationAttribute&lt;/code&gt; 本身也只是一个给编译器看的特性，不在 Runtime 里面定义，且编译器也不关心这个类型的可见性，编译器只关心特性的全名，即命名空间和类型名符合要求即可。那就直接开森地使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;file&lt;/code&gt; 关键字进行修饰，放入到对应的生成的代码所在文件里面。这样也可以防止定义的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InterceptsLocationAttribute&lt;/code&gt; 特性去污染其他源代码生成器或项目里面的代码&lt;/p&gt;

&lt;p&gt;完成了生成代码的拼接，为了将其进行返回，这里再定义一个名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GeneratedCodeInfo&lt;/code&gt; 的结构体，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;GeneratedCodeInfo&lt;/span&gt;&lt;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;GeneratedCode&lt;/span&gt;&lt;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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;将其作为返回值返回，且再叠加一个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Where&lt;/code&gt; 方法过滤掉不符合条件的情况&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;IncrementalValuesProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratedCodeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sourceProvider&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;SyntaxProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateSyntaxProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&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;_&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;span class=&quot;n&quot;&gt;syntaxContext&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;)&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;invocationExpressionSyntax&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvocationExpressionSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxContext&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;ArgumentSyntax&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argumentSyntax&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;invocationExpressionSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ArgumentList&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;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;argument&lt;/span&gt; &lt;span class=&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;syntaxContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SemanticModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetConstantValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argumentSyntax&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;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;generatedCode&lt;/span&gt; &lt;span class=&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;GeneratedCodeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;generatedCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;FooInterceptor&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;s&quot;&gt;.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;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;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;!=&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;最后将 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sourceProvider&lt;/code&gt; 值提供器注册到 RegisterImplementationSourceOutput 方法上即可。为什么是注册到 RegisterImplementationSourceOutput 方法上呢？因为这里面只是包含了具体的实现逻辑，没有任何可以参与语法分析的定义部分，也不会被外部所访问，放入到 RegisterImplementationSourceOutput 方法上十分合适&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterImplementationSourceOutput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sourceProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;productionContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&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;productionContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;provider&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;provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratedCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
           &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;通过以上的源代码生成器的代码，即可实现本演练中的拦截效果。最后尝试运行一下 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JuqawhicaqarLairciwholeni&lt;/code&gt; 控制台项目，可看到输出内容是&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;Interceptor1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lindexi&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doubi&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Interceptor2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lindexi&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doubi&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Interceptor3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lindexi&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doubi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这项 Interceptor 拦截器技术的介绍就到这里，拦截器技术现在还是有很多争议的，核心一点是破坏原本的静态代码阅读能力。静态阅读代码，不运行不构建时，所见的代码认为的运行效果不等于最终执行效果。这将会给很多开发者带来困惑，甚至可能被用于恶意代码的隐藏。但是在某些场景下，拦截器技术是非常有用的，比如在构建过程中进行日志记录、性能监控、权限控制等等，将原本影响性能的代码使用拦截器重新实现生成，不破坏原本代码结构等等&lt;/p&gt;

&lt;p&gt;整个使用 Interceptor 拦截器的源代码生成器的代码如下，同样是为了让我的博客引擎开森，我将以下代码的两个连在一起的花括号替换为全角的花括号&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;Generator&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;FooIncrementalGenerator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IIncrementalGenerator&lt;/span&gt;
&lt;span class=&quot;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;IncrementalGeneratorInitializationContext&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;IncrementalValuesProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratedCodeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sourceProvider&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;SyntaxProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateSyntaxProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&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;_&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;node&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InvocationExpressionSyntax&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;invocationExpressionSyntax&lt;/span&gt;&lt;span class=&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;invocationExpressionSyntax&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;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MemberAccessExpressionSyntax&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;memberAccessExpressionSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;// 这是一个调用名为 WriteLine 的方法代码，但就不知道具体是谁的 WriteLine 了。语法过程中是无法知道具体的类型是哪个的&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;// 比如 Foo a = ...; a.WriteLine(...);&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;// 或 Foo b = ...; b.WriteLine(...);&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;// 此时最多在语法层面只判断出是 WriteLine 方法，进一步判断就交给语义过程了&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;memberAccessExpressionSyntax&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;Identifier&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;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;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;n&quot;&gt;syntaxContext&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;)&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;symbolInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SemanticModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetSymbolInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;syntaxContext&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;symbolInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Symbol&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;IMethodSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methodSymbol&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// 以下这句判断纯属多余，因为语法过程中已经判断了是 WriteLine 方法&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methodSymbol&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;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;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;GeneratedCodeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;c1&quot;&gt;// 语义过程继续判断具体是否 Foo 类型的 WriteLine 方法&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;methodSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContainingType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToDisplayString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SymbolDisplayFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullyQualifiedFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&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;s&quot;&gt;&quot;global::JuqawhicaqarLairciwholeni.Foo&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;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratedCodeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;cm&quot;&gt;/*
                   class Foo
                   {
                       public void WriteLine(int message)
                       {
                           Console.WriteLine($&quot;Foo: {message}&quot;);
                       }
                   }
                 */&lt;/span&gt;

                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;invocationExpressionSyntax&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvocationExpressionSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxContext&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;ArgumentSyntax&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argumentSyntax&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;invocationExpressionSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ArgumentList&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;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;argument&lt;/span&gt; &lt;span class=&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;syntaxContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SemanticModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetConstantValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argumentSyntax&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;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!;&lt;/span&gt;

&lt;span class=&quot;cp&quot;&gt;#pragma warning disable RSEXPERIMENTAL002 // 实验性警告，忽略即可
&lt;/span&gt;                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interceptableLocation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SemanticModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetInterceptableLocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;invocationExpressionSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)!;&lt;/span&gt;

                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;displayLocation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interceptableLocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetDisplayLocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generatedCode&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;&quot;&quot;
&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;Foo_JuqawhicaqarLairciwholeni&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;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FooInterceptor&lt;/span&gt;
                          &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                              &lt;span class=&quot;c1&quot;&gt;// ｛｛displayLocation｝｝&lt;/span&gt;
                              &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InterceptsLocation&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;err&quot;&gt;｛｛&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;interceptableLocation&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;err&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;s&quot;&gt;&quot;｛｛interceptableLocation.Data｝｝&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;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InterceptorMethod&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;｛｛&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argument&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;this&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;｛｛&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;err&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;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;param&lt;/span&gt;&lt;span class=&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;Interceptor｛｛argument｝｝: lindexi is doubi&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;k&quot;&gt;namespace&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;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;true&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;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;InterceptsLocationAttribute&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;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InterceptsLocationAttribute&lt;/span&gt;&lt;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;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;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;_&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;_&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;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;&quot;;
&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;GeneratedCodeInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;generatedCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;FooInterceptor&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;s&quot;&gt;.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;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;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;!=&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;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterImplementationSourceOutput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sourceProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;productionContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&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;productionContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;provider&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;provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GeneratedCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
           &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;GeneratedCodeInfo&lt;/span&gt;&lt;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;GeneratedCode&lt;/span&gt;&lt;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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;更多拦截器技术的介绍请参阅： &lt;a href=&quot;https://github.com/dotnet/roslyn/blob/main/docs/features/interceptors.md&quot;&gt;Interceptors document&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;本演练代码放在 &lt;a href=&quot;https://github.com/lindexi/lindexi_gd/tree/f242a711c0f2fb65a01406a36042d87fc314cb51/Roslyn/JuqawhicaqarLairciwholeni&quot;&gt;github&lt;/a&gt; 和 &lt;a href=&quot;https://gitee.com/lindexi/lindexi_gd/tree/f242a711c0f2fb65a01406a36042d87fc314cb51/Roslyn/JuqawhicaqarLairciwholeni&quot;&gt;gitee&lt;/a&gt; 上，可以使用如下命令行拉取代码。我整个代码仓库比较庞大，使用以下命令行可以进行部分拉取，拉取速度比较快&lt;/p&gt;

&lt;p&gt;先创建一个空文件夹，接着使用命令行 cd 命令进入此空文件夹，在命令行里面输入以下代码，即可获取到本文的代码&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;git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin f242a711c0f2fb65a01406a36042d87fc314cb51
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上使用的是国内的 gitee 的源，如果 gitee 不能访问，请替换为 github 的源。请在命令行继续输入以下代码，将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码，可以发邮件向我要代码&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;git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin f242a711c0f2fb65a01406a36042d87fc314cb51
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;获取代码之后，进入 Roslyn/JuqawhicaqarLairciwholeni 文件夹，即可获取到源代码&lt;/p&gt;

&lt;p&gt;以上介绍的都是从代码入手，通过对现有的代码进行分析而生成新的代码。大家是否好奇其输入源还有没有其他方式。接下来将通过演练的方式和大家分别介绍从 csproj 等项目属性配置以及通过其他非代码文件的方式进行源代码生成&lt;/p&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-csharp highlighter-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;BuildAt=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BuildInformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BuildAt&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;Platform=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BuildInformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Platform&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;Configuration=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BuildInformation&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;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;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;BuildAt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2025&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;9&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;m&quot;&gt;41&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;n&quot;&gt;Platform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnyCpu&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;Release&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上的 BuildInformation 类型就是一个由源代码生成器生成的类，里面包含了构建时间、平台和配置信息&lt;/p&gt;

&lt;p&gt;在源代码生成器里面，不需要直接碰触 csproj 项目文件的读取，取而代之的是从 CompilationProvider 值提供器里面获取到项目的编译信息&lt;/p&gt;

&lt;p&gt;这里也能和大家证明的是，作为源代码生成器的输入源，不仅仅是代码，还可以是其他的一些信息。这里的信息是编译信息，也可以是其他的一些信息，比如额外的文件等等信息&lt;/p&gt;

&lt;p&gt;此演练的核心实现方法如下，首先是从 CompilationProvider 值提供器里面获取到项目的编译信息，接着就可以愉快地写入生成的代码啦，非常简单。这里我就跳过了项目创建的步骤，直接到核心代码的实现&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;Generator&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;FooGenerator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IIncrementalGenerator&lt;/span&gt;
    &lt;span class=&quot;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;IncrementalGeneratorInitializationContext&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;compilerOptions&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;CompilationProvider&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;s&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;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;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;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;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;compilerOptions&lt;/code&gt; 就包含了构建配置信息，如 Platform 和 Configuration 等信息。当然了以上的这句 Select 纯属卖萌，没有挑拣出任何有用信息，也没有做转换，不符合最佳实践，只能作为演示&lt;/p&gt;

&lt;p&gt;接下来就直接将 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;compilerOptions&lt;/code&gt; 放入到 RegisterSourceOutput 方法里面，进行生成源代码，如以下代码&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterSourceOutput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compilerOptions&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;n&quot;&gt;productionContext&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;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;code&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;using System;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;using System.Globalization;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;public static class BuildInformation&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;summary&amp;gt;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;    /// Returns the build date (UTC).&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;    /// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;    public static readonly DateTime BuildAt = DateTime.ParseExact(&quot;&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;UtcNow&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;&quot;, &quot;&quot;O&quot;&quot;, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;    /// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;    /// Returns the platform.&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;    /// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;    public const string Platform = &quot;&quot;&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;n&quot;&gt;Platform&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;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;    /// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;    /// Returns the configuration.&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;    /// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;    public const string Configuration = &quot;&quot;&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;n&quot;&gt;OptimizationLevel&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;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;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

                &lt;span class=&quot;n&quot;&gt;productionContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LinkDotNet.BuildInformation.g&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&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;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;这就完成了生成了一个名为 BuildInformation 的静态类，且此静态类还没有包含在任何的命名空间里面&lt;/p&gt;

&lt;p&gt;大家如果对更多细节感兴趣，还请参阅 &lt;a href=&quot;/post/IIncrementalGenerator-%E5%A2%9E%E9%87%8F-Source-Generator-%E7%94%9F%E6%88%90%E4%BB%A3%E7%A0%81%E5%BA%94%E7%94%A8-%E5%B0%86%E6%9E%84%E5%BB%BA%E6%97%B6%E9%97%B4%E5%86%99%E5%85%A5%E6%BA%90%E4%BB%A3%E7%A0%81.html&quot;&gt;IIncrementalGenerator 增量 Source Generator 生成代码应用 将构建时间写入源代码&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;以上就是采用 CompilationProvider 间接读取 csproj 项目文件的属性配置，将自定义配置内容写入源代码的过程。接下来将继续通过演练的方式，告诉大家如何在分析器项目里面读取其他非代码文件的内容&lt;/p&gt;

&lt;h2 id=&quot;演练写一个-禁用api调用-分析器&quot;&gt;演练：写一个 禁用API调用 分析器&lt;/h2&gt;

&lt;p&gt;前面介绍的都是围绕着编写源代码生成器展开的，本章将介绍使用专用分析器技术编写一个纯分析器。这个过程中也会介绍如何读取其他非代码文件的内容作为输入源的方式&lt;/p&gt;

&lt;p&gt;在前面的章节有和大家演示过调用 ReportDiagnostic 给出分析报告的方法。在源代码生成器里面给出的分析报告的步骤是进行语法和语义的分析，判断符合某个条件，则给出分析报告的结果。整个过程是非常公式化的。只不过在源代码生成器步骤里面更加侧重如何进行生成代码，从而需要许多细节的分析语法和语义的过程。专用的分析器则可以更大程度地省略掉这些琐碎的步骤，让大家可以使用根据方便的高级的 API 进行快速的分析语法语义&lt;/p&gt;

&lt;p&gt;为了能够更好地介绍专用分析器，在本章演练过程中，咱将带着这样的一个任务开始：编写一个禁用API调用分析器&lt;/p&gt;

&lt;p&gt;具体的任务需求细节是根据配置的禁用 API 调用文件里面记录的禁用列表，扫描整个项目里面，如果有哪个代码访问了在禁用 API 调用文件记录的禁用方法列表，则给出错误提示&lt;/p&gt;

&lt;p&gt;这个需求任务可以强行拆分为两步，第一步是获取到禁用 API 调用文件里面记录的禁用列表，第二步的扫描分析代码调用关系&lt;/p&gt;

&lt;p&gt;先不着急建立分析器项目，为了能够让大家更好地理解本演练的内容，这里选择先搭建好一个用于测试的项目，我这里创建名为 NelbecarballReanallyerhohe 的控制台项目，在控制台项目里面存放一个名为 BanList.txt 的文件。这个文件只是一个默认的文本文件，里面存放了一条禁用 API 调用的记录，如以下内容，禁用的是控制台输出的 WriteLine 方法&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;System.Console.WriteLine
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;其含义就是如果在当前项目里面，一旦有代码调用了 System.Console.WriteLine 方法，就会给出错误提示&lt;/p&gt;

&lt;p&gt;在 Program.cs 里面保持原样的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Console.WriteLine(&quot;Hello, World!&quot;);&lt;/code&gt; 输出。尝试构建项目，要求给出错误提示，告诉开发者不能调用被禁用的 System.Console.WriteLine 方法，如输出以下错误内容&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;error Ban01: 不能调用禁用的 API 哦，WriteLine 被 BanList.txt 标记禁用
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上就是整个演练的任务需求，这是一个非常经典的分析器任务。也适用于在真实项目里面做 API 约束。比如现在咱正在编写的分析器项目的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EnforceExtendedAnalyzerRules&lt;/code&gt; 限制属性，其实现原理也和本章将要介绍的具体技术十分接近。如果大家忘了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EnforceExtendedAnalyzerRules&lt;/code&gt; 属性，还请向前翻翻，或参阅 &lt;a href=&quot;/post/Roslyn-%E5%88%86%E6%9E%90%E5%99%A8-EnforceExtendedAnalyzerRules-%E5%B1%9E%E6%80%A7%E7%9A%84%E4%BD%9C%E7%94%A8.html&quot;&gt;Roslyn 分析器 EnforceExtendedAnalyzerRules 属性的作用&lt;/a&gt;
&lt;!-- [Roslyn 分析器 EnforceExtendedAnalyzerRules 属性的作用 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/17678673.html ) --&gt;&lt;/p&gt;

&lt;p&gt;开始编写专用分析器。搭建项目的方式和上文介绍的源代码生成器一样，也如上文所述，源代码生成器和分析器本身就一体的。在一个项目里面同时存在专用分析器和源代码生成器是完全被允许的，也是被推荐的做法。这里就不再详细展开项目的创建方法了，如需整个项目代码，可在本章末尾找到本章所有代码的下载拉取方法&lt;/p&gt;

&lt;!--  调试分析器和调试源代码生成器的方法也是完全相同的 --&gt;
&lt;p&gt;在分析器项目里面创建一个名为 BanAPIAnalyzer 的类型，让其继承自 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer&lt;/code&gt; 类型。如此即可让 BanAPIAnalyzer 成为专用分析器。继承之后，就需要实现 DiagnosticAnalyzer 抽象类型的 SupportedDiagnostics 属性和 Initialize 方法&lt;/p&gt;

&lt;p&gt;在专用分析器的设计里面，要求实现 SupportedDiagnostics 属性，告诉框架层当前这个专用分析器类型支持哪些 Diagnostic 内容。一旦 IDE 等配置某些 Diagnostic 被禁用时，如果当前的这个专用分析器的所有 Diagnostic 内容都被禁用，则此专用分析器将不会被启用。另一个作用则是可以在 VisualStudio 的分析器列表里面枚举查看信息，方便配置。比如通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.editorconfig&lt;/code&gt; 文件，将原本是错误等级的 Diagnostic 设置为警告等级&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;# 演示在 .editorconfig 文件将原本是 error 等级的 Ban01 设置为 warning 等级
dotnet_diagnostic.Ban01.severity = warning
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在本演练这里，只添加一个 DiagnosticDescriptor 到 SupportedDiagnostics 属性。如任务需求所述，本演练这里只有一个禁用 API 调用的分析。创建 DiagnosticDescriptor 的方法在上文已经有详细介绍了，上文介绍的是支持多语言的创建方式，相对来说比较繁琐，但也正式。我在这里和大家介绍另一个方式，就是只有单个语言的固定字符串方式，当然了，这样的方式适用范围肯定更小了，难以全球化使用&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;BanAPIAnalyzer&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;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;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;{&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;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;DiagnosticDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Ban01&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;CallBanAPI&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;不能调用禁用的 API 哦，{0} 被 {1} 标记禁用&quot;&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;s&quot;&gt;&quot;Error&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;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;和源代码生成器一样，核心的代码实现放在 Initialize 方法里面。这两个 Initialize 不同点在于其方法参数上，以下是专用分析器的 Initialize 方法的签名&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;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;通常的专用分析器在 Initialize 的第一句话是调用 AnalysisContext 的 EnableConcurrentExecution 方法。这句话的作用是告诉框架层，当前的专用分析器是线程安全的，可以多线程并发执行。这样可以提高分析器的执行效率，但也要求开发者在编写专用分析器的时候要保证线程安全。不过在调试过程中，却通常将其注释掉，防止多线程进入让调试困难&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;BanAPIAnalyzer&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;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;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;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;{&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;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;DiagnosticDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Ban01&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;CallBanAPI&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;不能调用禁用的 API 哦，{0} 被 {1} 标记禁用&quot;&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;s&quot;&gt;&quot;Error&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;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;nf&quot;&gt;ToImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在 Initialize 的第二句话通常是配置是否对源代码生成器生成的源代码进行分析，默认咱选 None 即可，如以下代码&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;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;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;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;喜欢点点点的伙伴也许看到了在 AnalysisContext 类型里面充满了各种 RegisterXxxAction 的方法。这些方法的作用都是寻找一个注入点，即在什么时机进行分析、在什么范围内进行分析。越是靠后的时机，能够拿到的信息越多，但其分析提示可能就不够及时。具体选用什么时机进行分析，如果把握不准的话，大家可以先选一个比较靠后的时机，调试成功之后再逐渐选择比较靠前的时机。这里的靠前和靠后相对的是构建时机。在本演练里面，为了演示方便，就选用了很是靠后的 RegisterCompilationStartAction 时机，在这里能够拿到完全的 AdditionalFile 附加文件，即 BanList.txt 文件&lt;/p&gt;

&lt;p&gt;现在开始这个专用分析器实现的第一步，获取到禁用 API 调用文件里面记录的禁用列表。在咱这个专用分析器里面，比较灵活，允许开发者设置哪个文件记录的就是禁用 API 列表的文件。如在控制台项目（敲黑板，非分析器项目，是那个被分析的项目）里面的 csproj 项目文件通过如下代码指定 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BanList.txt&lt;/code&gt; 就是记录禁用 API 列表的文件&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div 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;BanAPIFileName&amp;gt;&lt;/span&gt;BanList.txt&lt;span class=&quot;nt&quot;&gt;&amp;lt;/BanAPIFileName&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;language-plaintext highlighter-rouge&quot;&gt;$(PackageId).targets&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$(PackageId).props&lt;/code&gt; 文件。但咱这里没有通过 NuGet 进行分发，因此一些可以放在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$(PackageId).targets&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$(PackageId).props&lt;/code&gt; 文件的杂活就需要放入到被分析的项目里面。详细关于如何打包 NuGet 进行分发，我将在下文详细和大家介绍&lt;/p&gt;

&lt;p&gt;为什么会提到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$(PackageId).targets&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$(PackageId).props&lt;/code&gt; 文件的杂活呢？这是因为咱在被分析的控制台项目的 csproj 项目文件配置的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BanAPIFileName&lt;/code&gt; 属性内容，默认情况下是无法直接被分析器项目感知到的，添加的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BanList.txt&lt;/code&gt; 文件也无法被分析器直接感知到。为了让分析器能够拿到配置的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BanAPIFileName&lt;/code&gt; 属性，以及 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BanList.txt&lt;/code&gt; 文件，就需要在被分析的控制台项目的 csproj 项目文件里面添加额外的配置&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;CompilerVisibleProperty ： 用于标记有哪些 Property 可以被分析器感知到&lt;/li&gt;
  &lt;li&gt;AdditionalFiles ： 添加用于让分析器项目使用的附加文件，官方翻译为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;分析器其他文件&lt;/code&gt; 或 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C# 分析器其他文件&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;按照以上描述可以了解到，咱需要通过 CompilerVisibleProperty 标记 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BanAPIFileName&lt;/code&gt; 属性，使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AdditionalFiles&lt;/code&gt; 添加 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BanList.txt&lt;/code&gt; 文件，即让 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BanList.txt&lt;/code&gt; 使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C# 分析器其他文件&lt;/code&gt; 生成方式，如下图所示&lt;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门17.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20253171530568088.jpg&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;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;BanAPIFileName&amp;gt;&lt;/span&gt;BanList.txt&lt;span class=&quot;nt&quot;&gt;&amp;lt;/BanAPIFileName&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;CompilerVisibleProperty&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BanAPIFileName&quot;&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;AdditionalFiles&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BanList.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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门16.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2025317858467937.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;对于一个通过 NuGet 分发的分析器项目，则是会在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$(PackageId).props&lt;/code&gt; 文件里面存放 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;CompilerVisibleProperty Include=&quot;BanAPIFileName&quot; /&amp;gt;&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;AdditionalFiles Include=&quot;BanList.txt&quot; /&amp;gt;&lt;/code&gt; 这两个配置，而不需要被分析项目添加这些杂活&lt;/p&gt;

&lt;p&gt;以上介绍的 CompilerVisibleProperty 和 AdditionalFiles 是对整个分析器生效，即无论是专用分析器还是源代码生成器，这部分知识内容都完全相同&lt;/p&gt;

&lt;p&gt;如对在分析器项目里面读取 PropertyGroup 里面的 Property 感兴趣，还请参阅 &lt;a href=&quot;/post/IIncrementalGenerator-%E5%A2%9E%E9%87%8F-Source-Generator-%E7%94%9F%E6%88%90%E4%BB%A3%E7%A0%81%E5%85%A5%E9%97%A8-%E8%AF%BB%E5%8F%96-csproj-%E9%A1%B9%E7%9B%AE%E6%96%87%E4%BB%B6%E7%9A%84%E5%B1%9E%E6%80%A7%E9%85%8D%E7%BD%AE.html&quot;&gt;读取 csproj 项目文件的属性配置方法&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;完成被分析的控制台项目的 csproj 项目文件配置之后，就可以在 RegisterCompilationStartAction 方法内通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BanAPIFileName&lt;/code&gt; 属性了解到哪个 AdditionalFiles 就是标记禁用列表的文件，其代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;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;None&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;RegisterCompilationStartAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompilationStartAnalysisContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;analysisContext&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;analysisContext&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;n&quot;&gt;AnalyzerConfigOptionsProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GlobalOptions&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;s&quot;&gt;&quot;build_property.BanAPIFileName&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;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;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;再遍历 AdditionalFiles 找到记录禁用 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;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RegisterCompilationStartAction&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;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;analysisContext&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;n&quot;&gt;AnalyzerConfigOptionsProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GlobalOptions&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;s&quot;&gt;&quot;build_property.BanAPIFileName&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;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;n&quot;&gt;AdditionalText&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;analysisContext&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;n&quot;&gt;AdditionalFiles&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;t&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;GetFileName&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;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;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;file&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;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;也许有伙伴表示，这样的实现方法需要两步，第一步是读取 Property 属性，第二步是遍历 AdditionalFiles 文件，相对来说比较麻烦。能否直接在 AdditionalFiles 里面标记配置呢？即如以下的写法&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div 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;!-- 不要 BanAPIFileName 属性，直接在文件标记 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- &amp;lt;BanAPIFileName&amp;gt;BanList.txt&amp;lt;/BanAPIFileName&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;!-- 不要 BanAPIFileName 属性，自然也就不要 CompilerVisibleProperty 配置 --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- &amp;lt;CompilerVisibleProperty Include=&quot;BanAPIFileName&quot; /&amp;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;AdditionalFiles&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BanList.txt&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;IsBanAPIFileName=&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这当然是完全可以的啦，只不过其读取方法需要更改一下，按照 &lt;a href=&quot;/post/Roslyn-%E5%88%86%E6%9E%90%E5%99%A8-%E8%AF%BB%E5%8F%96-csproj-%E9%A1%B9%E7%9B%AE%E6%96%87%E4%BB%B6%E7%9A%84-AdditionalFiles-Item-%E7%9A%84-Metadata-%E9%85%8D%E7%BD%AE.html&quot;&gt;Roslyn 分析器 读取 csproj 项目文件的 AdditionalFiles Item 的 Metadata 配置&lt;/a&gt; 进行更改。一般情况下不会这么写的，因为在有 NuGet 分发的帮助下，将杂活放入到 props 文件里面，整个实际的被分析项目只有编写 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;BanAPIFileName&amp;gt;BanList.txt&amp;lt;/BanAPIFileName&amp;gt;&lt;/code&gt; 这一句配置，相对来说会比写 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;AdditionalFiles Include=&quot;BanList.txt&quot; IsBanAPIFileName=&quot;True&quot;/&amp;gt;&lt;/code&gt; 更加方便&lt;/p&gt;

&lt;p&gt;获取到了配置禁用 API 列表的文件之后，即可通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AdditionalText.GetText&lt;/code&gt; 方法获取到文件内容，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;AdditionalText&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;SourceText&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sourceText&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;GetText&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;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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里依然和源代码生成器一样，将令牌传入到调用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AdditionalText.GetText&lt;/code&gt; 方法里面，避免读取文件过程中被分析的内容变更导致白白读取内容进行空等&lt;/p&gt;

&lt;p&gt;进一步的，直接获取 SourceText 的 Lines 属性，即可拿到一行一个禁用 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;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;analysisContext&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;n&quot;&gt;AdditionalFiles&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;t&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;GetFileName&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;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;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;file&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;ImmutableHashSet&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;banSet&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;GetText&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;Select&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;n&quot;&gt;t&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;ToImmutableHashSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ImmutableHashSet&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;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;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;以上代码就是获取到了禁用 API 列表的文件内容，接下来就是扫描分析代码调用关系，进入到这个专用分析器实现的第二步。这个过程和源代码生成器的分析过程类似，只不过这里不需要生成代码，而是给出分析报告。只是给出分析结果，咱有更多便捷的方法可用，不再和源代码生成器的分析一样逐个爬语法树或者语义树的节点，而是直接使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RegisterSyntaxNodeAction&lt;/code&gt; 方法，给定感兴趣的分析内容点&lt;/p&gt;

&lt;p&gt;在咱本次演练内容里面，核心就是判断调用的方法是否在禁用列表里面。即感兴趣的分析内容点就是代码里面方法调用，即 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SyntaxKind.InvocationExpression&lt;/code&gt; 内容。于是在调用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RegisterSyntaxNodeAction&lt;/code&gt; 方法过程，将 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SyntaxKind.InvocationExpression&lt;/code&gt; 作为第二个参数传入，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;ImmutableHashSet&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;banSet&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;GetText&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;Select&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;n&quot;&gt;t&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;ToImmutableHashSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ImmutableHashSet&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;Empty&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;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;SyntaxNodeAnalysisContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nodeAnalysisContext&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;c1&quot;&gt;// 忽略其他代码&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;InvocationExpression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;由于在 RegisterSyntaxNodeAction 参数要求了是 InvocationExpression 类型，在 RegisterSyntaxNodeAction 的委托里面，就可以使用强转方式将 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nodeAnalysisContext.Node&lt;/code&gt; 转换为 InvocationExpressionSyntax 类型，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;analysisContext&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;nodeAnalysisContext&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;invocationExpression&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvocationExpressionSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nodeAnalysisContext&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;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;SyntaxKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvocationExpression&lt;/span&gt;&lt;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 离不开语义的辅助，从全语法层面是难以或无法知道具体调用的 API 是哪个的。通过 SemanticModel 获取语义符号，即可知道调用的 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;analysisContext&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;nodeAnalysisContext&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;invocationExpression&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvocationExpressionSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nodeAnalysisContext&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;symbolInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nodeAnalysisContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SemanticModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetSymbolInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;invocationExpression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;symbolInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Symbol&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;IMethodSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;symbol&lt;/span&gt;&lt;span class=&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;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;SyntaxKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvocationExpression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;理论上这里拿到的 Symbol 必然是 IMethodSymbol 且不是空的&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;analysisContext&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;nodeAnalysisContext&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;invocationExpression&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvocationExpressionSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nodeAnalysisContext&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;symbolInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nodeAnalysisContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SemanticModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetSymbolInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;invocationExpression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;symbolInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Symbol&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;IMethodSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;symbol&lt;/span&gt;&lt;span class=&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;containingType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;symbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContainingType&lt;/span&gt;&lt;span class=&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;n&quot;&gt;SyntaxKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvocationExpression&lt;/span&gt;&lt;span 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;containingType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;symbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContainingType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;symbolDisplayFormat&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SymbolDisplayFormat&lt;/span&gt;
&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;SymbolDisplayGlobalNamespaceStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Omitted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 命名空间之前加上 global 防止冲突&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SymbolDisplayTypeQualificationStyle&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NameAndContainingTypesAndNamespaces&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;containingTypeName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;containingType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToDisplayString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;symbolDisplayFormat&lt;/span&gt;&lt;span class=&quot;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;symbol&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;methodName&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;containingTypeName&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;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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如此即可拿到方法全名，即 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;命名空间.类型.方法名&lt;/code&gt; 的格式。再进入 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;banSet&lt;/code&gt; 集合判断一下即可了解当前的调用方法是否在禁用了集合中&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;methodName&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;containingTypeName&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;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;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;banSet&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;methodName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&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;一旦判断当前调用的方法在禁用集合内，则给出错误信息。给出错误信息时，可选列出代码所在的文件、第几行第几列的 Location 信息。从 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SyntaxNodeAnalysisContext.Node&lt;/code&gt; 创建出 Location 对象，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;location&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;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nodeAnalysisContext&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;SyntaxTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nodeAnalysisContext&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;FullSpan&lt;/span&gt;&lt;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;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;不能调用禁用的 API 哦，{0} 被 {1} 标记禁用&quot;&lt;/code&gt; 里面的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{0}&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{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;nodeAnalysisContext&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;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SupportedDiagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;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;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;messageArgs&lt;/span&gt;&lt;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;symbol&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;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;如此即可完成本演练的禁止调用禁用列表的 API 的分析器，整个 BanAPIAnalyzer 类的代码如下，可以看到使用很少的代码量就能实现此功能&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;BanAPIAnalyzer&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;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;None&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;RegisterCompilationStartAction&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;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;analysisContext&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;n&quot;&gt;AnalyzerConfigOptionsProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GlobalOptions&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;s&quot;&gt;&quot;build_property.BanAPIFileName&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;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;file&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;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;n&quot;&gt;AdditionalFiles&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;t&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;GetFileName&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;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;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;file&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;ImmutableHashSet&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;banSet&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;GetText&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;Select&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;n&quot;&gt;t&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;ToImmutableHashSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;ImmutableHashSet&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;Empty&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;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;nodeAnalysisContext&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;invocationExpression&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvocationExpressionSyntax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nodeAnalysisContext&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;symbolInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nodeAnalysisContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SemanticModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetSymbolInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;invocationExpression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;symbolInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Symbol&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;IMethodSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;symbol&lt;/span&gt;&lt;span class=&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;containingType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;symbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContainingType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

                        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;symbolDisplayFormat&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SymbolDisplayFormat&lt;/span&gt;
                        &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;SymbolDisplayGlobalNamespaceStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Omitted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                            &lt;span class=&quot;c1&quot;&gt;// 命名空间之前加上 global 防止冲突&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;SymbolDisplayTypeQualificationStyle&lt;/span&gt;
                                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NameAndContainingTypesAndNamespaces&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;containingTypeName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;containingType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToDisplayString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;symbolDisplayFormat&lt;/span&gt;&lt;span class=&quot;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;symbol&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;methodName&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;containingTypeName&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;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;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;banSet&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;methodName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;kt&quot;&gt;var&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;Location&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;nodeAnalysisContext&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;SyntaxTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nodeAnalysisContext&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;FullSpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                            &lt;span class=&quot;n&quot;&gt;nodeAnalysisContext&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;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SupportedDiagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;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;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;messageArgs&lt;/span&gt;&lt;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;symbol&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;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;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;InvocationExpression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&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;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;{&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;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;DiagnosticDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Ban01&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;CallBanAPI&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;不能调用禁用的 API 哦，{0} 被 {1} 标记禁用&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Error&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;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;ToImmutableArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span 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/lindexi/lindexi_gd/tree/ed27dcda954d4baed58c74b9c1e355468c7135fc/Roslyn/NelbecarballReanallyerhohe&quot;&gt;github&lt;/a&gt; 和 &lt;a href=&quot;https://gitee.com/lindexi/lindexi_gd/tree/ed27dcda954d4baed58c74b9c1e355468c7135fc/Roslyn/NelbecarballReanallyerhohe&quot;&gt;gitee&lt;/a&gt; 上，可以使用如下命令行拉取代码。我整个代码仓库比较庞大，使用以下命令行可以进行部分拉取，拉取速度比较快&lt;/p&gt;

&lt;p&gt;先创建一个空文件夹，接着使用命令行 cd 命令进入此空文件夹，在命令行里面输入以下代码，即可获取到本文的代码&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;git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin ed27dcda954d4baed58c74b9c1e355468c7135fc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上使用的是国内的 gitee 的源，如果 gitee 不能访问，请替换为 github 的源。请在命令行继续输入以下代码，将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码，可以发邮件向我要代码&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;git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin ed27dcda954d4baed58c74b9c1e355468c7135fc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;获取代码之后，进入 Roslyn/NelbecarballReanallyerhohe 文件夹，即可获取到源代码&lt;/p&gt;

&lt;!-- 
为了能够更好地介绍专用分析器，在本章演练过程中，咱将带着这样的一个任务开始：编写一个 API 实现约束分析器。这个分析器的功能是检查代码中是否符合协议约定的按照某个顺序实现了某些 API 成员

这个任务的背景是

现实中我也有一个项目完全地使用了本章介绍的禁用API调用的

介绍分析器
介绍更加明确的分析器--&gt;

&lt;p&gt;既然有了分析器，可以给开发者报告出一些警告或错误信息，那是否还能自动帮助开发者修复这些问题呢？这就需要用到超过本文范围的 代码修改器 知识了。编写代码修改器是另外的故事了，这里就不展开了，如果大家对此感兴趣，可以参阅 &lt;a href=&quot;https://blog.walterlv.com/post/comment-analyzer-and-code-fix-using-roslyn.html&quot;&gt;使用 Roslyn 分析代码注释，给 TODO 类型的注释添加负责人、截止日期和 issue 链接跟踪 - walterlv&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;/p&gt;

&lt;!-- ![](image/dotnet 用 SourceGenerator 源代码生成技术实现中文编程语言/dotnet 用 SourceGenerator 源代码生成技术实现中文编程语言0.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20221071148534467.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果大家感觉这个效果很酷，那请参阅 &lt;a href=&quot;/post/dotnet-%E7%94%A8-SourceGenerator-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E7%8E%B0%E4%B8%AD%E6%96%87%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80.html&quot;&gt;dotnet 用 SourceGenerator 源代码生成技术实现中文编程语言&lt;/a&gt; 文章，里面详细介绍了如何通过源代码生成技术实现中文编程语言&lt;/p&gt;

&lt;h2 id=&quot;打包-nuget-包进行分发&quot;&gt;打包 NuGet 包进行分发&lt;/h2&gt;

&lt;p&gt;学习了很多源代码生成器和分析器的知识，相信此时大家也很想编写和发布一个自己的源代码生成器或分析器。按照 dotnet 里面的惯例，各种产物都会通过 NuGet 的形式进行发布，自然也包括源代码生成器和分析器。接下来我将和大家介绍如何将自己编写的源代码生成器分析器打包成 NuGet 包进行分发&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;c&quot;&gt;&amp;lt;!-- 
      配置为无依赖。即避免带上 TargetFramework=netstandard2.0 的限制
      配合 IncludeBuildOutput=false 即可让任意项目引用，无视目标框架
    --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;SuppressDependenciesWhenPacking&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/SuppressDependenciesWhenPacking&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 不要将输出文件放入到 nuget 的 lib 文件夹下 --&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;!-- 不要警告 lib 下没内容 --&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中核心为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IncludeBuildOutput&lt;/code&gt; 属性，表示不要将分析器输出程序集放入到 NuGet 包的 lib 文件夹下。一旦被放入到 NuGet 包的 lib 文件夹下，将会让安装了此 NuGet 包的项目引用了分析器程序集，而不会将分析器程序集作为分析器运行&lt;/p&gt;

&lt;p&gt;现在如果就直接打出来 NuGet 包，则会看到 NuGet 包是一个空包，什么有用的内容都没有包含。这是因为分析器项目的输出程序集还没被作为分析器内容打入到 NuGet 包里面。再添加以下代码，将分析器项目的输出程序集放入到 NuGet 包的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;analyzers/dotnet/cs&lt;/code&gt; 文件夹下，这样就可以让其他项目在安装到本 NuGet 包的时候，按照 NuGet 的约定，从 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;analyzers/dotnet/cs&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;AddOutputDllToNuGetAnalyzerFolder&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;c&quot;&gt;&amp;lt;!-- 
      以下这句 ItemGroup 不能放在 Target 外面。否则首次构建之前 $(OutputPath)\$(AssemblyName).dll 是不存在的
      这里需要选用在 _GetPackageFiles 之前，确保在 NuGet 收集文件之前，标记将输出的 dll 放入到 NuGet 的 analyzers 文件夹下
    --&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)\$(AssemblyName).dll&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;analyzers/dotnet/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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;大家可以看到，这里使用了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BeforeTargets=&quot;_GetPackageFiles&quot;&lt;/code&gt; 属性，表示在 NuGet 收集文件之前，将输出的 dll 放入到 NuGet 的 analyzers 文件夹下。为什么不能直接在 Project 下的一级 ItemGroup 里面添加呢？这是因为首次构建之前 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$(OutputPath)\$(AssemblyName).dll&lt;/code&gt; 是不存在的，直接打包将会输出空包。于是选定在 Build 构建之后，收集文件之前的时机，此最佳时机就是在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_GetPackageFiles&lt;/code&gt; 之前&lt;/p&gt;

&lt;p&gt;如果选用直接在 Project 下的一级 ItemGroup 里面添加 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$(OutputPath)\$(AssemblyName).dll&lt;/code&gt; 则不能一次构建出包，此时最推荐的是先做一次 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; 再做一次 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet pack --no-build&lt;/code&gt; 打包。由于这个命令过程需要拆分为两步，可能会漏掉导致行为不符合预期，因此我在本文里面就特意使用了 Target 的方式进行收集&lt;/p&gt;

&lt;p&gt;如果大家对于在 csproj 里面编写 Target 等逻辑不熟悉，还请参阅 &lt;a href=&quot;https://blog.walterlv.com/post/write-msbuild-target&quot;&gt;如何编写基于 Microsoft.NET.Sdk 的跨平台的 MSBuild Target（附各种自带的 Task） - walterlv&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;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;LangVersion&amp;gt;&lt;/span&gt;latest&lt;span class=&quot;nt&quot;&gt;&amp;lt;/LangVersion&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;EnforceExtendedAnalyzerRules&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/EnforceExtendedAnalyzerRules&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;IsRoslynComponent&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IsRoslynComponent&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Nullable&amp;gt;&lt;/span&gt;enable&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Nullable&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Version&amp;gt;&lt;/span&gt;1.0.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;/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;!-- [dotnet 打包 NuGet 的配置属性大全整理](/post/dotnet-%E6%89%93%E5%8C%85-NuGet-%E7%9A%84%E9%85%8D%E7%BD%AE%E5%B1%9E%E6%80%A7%E5%A4%A7%E5%85%A8%E6%95%B4%E7%90%86.html ) --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Copyright&amp;gt;&lt;/span&gt;Copyright (c) lindexi 2020-$([System.DateTime]::Now.ToString(`yyyy`))&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Copyright&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageLicenseExpression&amp;gt;&lt;/span&gt;MIT&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackageLicenseExpression&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReadmeFile&amp;gt;&lt;/span&gt;README.md&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackageReadmeFile&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Description&amp;gt;&lt;/span&gt;这里填写描述信息&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Description&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 
      配置为无依赖。即避免带上 TargetFramework=netstandard2.0 的限制
      配合 IncludeBuildOutput=false 即可让任意项目引用，无视目标框架
    --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;SuppressDependenciesWhenPacking&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/SuppressDependenciesWhenPacking&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- 不要将输出文件放入到 nuget 的 lib 文件夹下 --&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;!-- 不要警告 lib 下没内容 --&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;/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;..\..\..\README.md&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Link=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;README.md&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;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.LatestCSharpFeatures&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;12.0.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;Microsoft.CodeAnalysis.CSharp&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.11.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;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;Properties\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;DesignTime&amp;gt;&lt;/span&gt;True&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DesignTime&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;AutoGen&amp;gt;&lt;/span&gt;True&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AutoGen&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;DependentUpon&amp;gt;&lt;/span&gt;Resources.resx&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;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;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;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Generator&amp;gt;&lt;/span&gt;ResXFileCodeGenerator&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Generator&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;LastGenOutput&amp;gt;&lt;/span&gt;Resources.Designer.cs&lt;span class=&quot;nt&quot;&gt;&amp;lt;/LastGenOutput&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/EmbeddedResource&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;AddOutputDllToNuGetAnalyzerFolder&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;c&quot;&gt;&amp;lt;!-- 
      以下这句 ItemGroup 不能放在 Target 外面。否则首次构建之前 $(OutputPath)\$(AssemblyName).dll 是不存在的
      这里需要选用在 _GetPackageFiles 之前，确保在 NuGet 收集文件之前，标记将输出的 dll 放入到 NuGet 的 analyzers 文件夹下
    --&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)\$(AssemblyName).dll&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;analyzers/dotnet/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;第一个 PropertyGroup 块为分析器固有信息，其中的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;Version&amp;gt;1.0.0&amp;lt;/Version&amp;gt;&lt;/code&gt; 版本号被程序集和 NuGet 包版本号所共用。如果只为指定 NuGet 的包版本号而不影响程序集的版本号，可使用专用的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PackageVersion&lt;/code&gt; 属性&lt;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门33.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20253221529185015.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;第二个 PropertyGroup 块为 NuGet 包的信息。其中的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;Description&amp;gt;这里填写描述信息&amp;lt;/Description&amp;gt;&lt;/code&gt; 为 NuGet 包的描述信息，这个描述信息可以在 NuGet Package Explorer 里面直接看到，如下图所示，直接使用 NuGet Package Explorer 打开 NuGet 包，即可看到描述信息&lt;/p&gt;

&lt;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门34.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20253221531126363.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;而 ReadMe 文件记录则是需要与下方的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;None Include=&quot;..\..\..\README.md&quot; Link=&quot;README.md&quot; Pack=&quot;True&quot; PackagePath=&quot;\&quot;/&amp;gt;&lt;/code&gt; 配合才能完成，用于放入识别的 README.md 文件，帮助开发者入门使用此分析器 NuGet 包&lt;/p&gt;

&lt;p&gt;版权 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Copyright&lt;/code&gt; 信息里面，我使用了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$([System.DateTime]::Now.ToString(&lt;/code&gt;yyyy&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;))&lt;/code&gt; 语法，用于获取当前年份。这样可以保证每年都会更新版权信息，不需要手动修改，只需要在新年的时候重新打包即可&lt;/p&gt;

&lt;p&gt;再往下是 SuppressDependenciesWhenPacking 配置无依赖和 IncludeBuildOutput 配置不要将输出文件放入到 nuget 的 lib 文件夹等信息，这部分上文有描述，这里就不再赘述&lt;/p&gt;

&lt;p&gt;以上内容里面用到了很多 NuGet 打包相关属性，如对此感兴趣，还请参阅 &lt;a href=&quot;/post/dotnet-%E6%89%93%E5%8C%85-NuGet-%E7%9A%84%E9%85%8D%E7%BD%AE%E5%B1%9E%E6%80%A7%E5%A4%A7%E5%85%A8%E6%95%B4%E7%90%86.html&quot;&gt;dotnet 打包 NuGet 的配置属性大全整理&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;如果大家打不出来正确的 NuGet 包，可以拉取我的代码进行对比参考&lt;/p&gt;

&lt;p&gt;本章以上的代码放在 &lt;a href=&quot;https://github.com/lindexi/lindexi_gd/tree/7ee17551c750a643593f6f5e4a0d03f89456b393/Roslyn/NayijainawNerkanekajawi&quot;&gt;github&lt;/a&gt; 和 &lt;a href=&quot;https://gitee.com/lindexi/lindexi_gd/blob/7ee17551c750a643593f6f5e4a0d03f89456b393/Roslyn/NayijainawNerkanekajawi&quot;&gt;gitee&lt;/a&gt; 上，可以使用如下命令行拉取代码。我整个代码仓库比较庞大，使用以下命令行可以进行部分拉取，拉取速度比较快&lt;/p&gt;

&lt;p&gt;先创建一个空文件夹，接着使用命令行 cd 命令进入此空文件夹，在命令行里面输入以下代码，即可获取到本文的代码&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;git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 7ee17551c750a643593f6f5e4a0d03f89456b393
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上使用的是国内的 gitee 的源，如果 gitee 不能访问，请替换为 github 的源。请在命令行继续输入以下代码，将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码，可以发邮件向我要代码&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;git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 7ee17551c750a643593f6f5e4a0d03f89456b393
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;获取代码之后，进入 Roslyn/NayijainawNerkanekajawi 文件夹，即可获取到源代码&lt;/p&gt;

&lt;p&gt;以上是十分简单地将分析器打成 NuGet 包。如编写一个 禁用API调用 的分析器演练章节中所述，在制作 NuGet 包的过程可以附带 props 文件和 targets 文件，用于在安装 NuGet 包的时候，自动添加一些配置。这样就可以让分析器的使用更加方便，不需要安装了分析器的项目手动添加配置&lt;/p&gt;

&lt;p&gt;回顾一下原本在 禁用API调用 的分析器章节中的引用分析器的 NelbecarballReanallyerhohe 控制台项目的 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;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;BanAPIFileName&amp;gt;&lt;/span&gt;BanList.txt&lt;span class=&quot;nt&quot;&gt;&amp;lt;/BanAPIFileName&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;CompilerVisibleProperty&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BanAPIFileName&quot;&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;AdditionalFiles&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BanList.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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上内容里面的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;CompilerVisibleProperty Include=&quot;BanAPIFileName&quot; /&amp;gt;&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;AdditionalFiles Include=&quot;BanList.txt&quot; /&amp;gt;&lt;/code&gt; 都是固定配置内容。完全可以放入到分析器 NuGet 包里面，不需要每个安装了分析器的项目都手动添加这些重复的配置内容&lt;/p&gt;

&lt;p&gt;在分析器的 NuGet 包里面存放 props 文件和 targets 文件，将分析器所需配置放入到这两个文件里面，即可在安装完 NuGet 包之后，应用这两个文件里面的配置，避免安装分析器的项目需要添加固定配置内容&lt;/p&gt;

&lt;p&gt;依然是为了方便大家获取源代码，我这里重新拷贝 禁用API调用 的分析器演练章节的项目里面的代码，重新新建了名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JabeehuharKekajerlurlaw.Analyzer&lt;/code&gt; 的分析器项目和名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JabeehuharKekajerlurlaw&lt;/code&gt; 控制台项目&lt;/p&gt;

&lt;p&gt;首先在分析器项目里面新建一个名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Assets&lt;/code&gt; 的文件夹。这个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Assets&lt;/code&gt; 文件夹名仅仅只是我的喜好，大家可以根据自己的喜好换成其他的文件夹名。再在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Assets&lt;/code&gt; 文件夹里面新建两个文件，分别是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Package.props&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Package.targets&lt;/code&gt; 文件。这两个文件的内容如下&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Package.props&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;/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;language-plaintext highlighter-rouge&quot;&gt;Package.targets&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;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;CompilerVisibleProperty&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BanAPIFileName&quot;&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;AdditionalFiles&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(BanAPIFileName)&quot;&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;!-- ![](image/dotnet 源代码生成器分析器入门/dotnet 源代码生成器分析器入门32.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F20253221523435206.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;针对上文的 禁用API调用 的分析器演练的需求，完全将配置的信息写入到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Package.targets&lt;/code&gt; 文件里面，确保顺序足够靠后，在对应的安装了分析器的项目里面完成配置之后，再设置加入到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AdditionalFiles&lt;/code&gt; 文件里面&lt;/p&gt;

&lt;p&gt;完成 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Package.props&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Package.targets&lt;/code&gt; 文件的创建之后，将这两个文件按照 &lt;a href=&quot;/post/Roslyn-%E6%89%93%E5%8C%85%E8%87%AA%E5%AE%9A%E4%B9%89%E7%9A%84%E6%96%87%E4%BB%B6%E5%88%B0-NuGet-%E5%8C%85.html&quot;&gt;Roslyn 打包自定义的文件到 NuGet 包&lt;/a&gt; 博客的方法，将这两个文件打包到 NuGet 包的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build&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;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Assets\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\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;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;放入到 NuGet 包里面的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build&lt;/code&gt; 文件夹下，这样安装了分析器的项目就会自动引入这两个文件，依靠这两个文件提供的配置，不需要手动添加配置。以下是从原本项目引用方式分析器，更改为引用分析器 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;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;BanAPIFileName&amp;gt;&lt;/span&gt;BanList.txt&lt;span class=&quot;nt&quot;&gt;&amp;lt;/BanAPIFileName&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;CompilerVisibleProperty&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BanAPIFileName&quot;&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;AdditionalFiles&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BanList.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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&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;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;BanAPIFileName&amp;gt;&lt;/span&gt;BanList.txt&lt;span class=&quot;nt&quot;&gt;&amp;lt;/BanAPIFileName&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;可以看到，引用分析器 NuGet 包之后，不需要再手动添加 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;CompilerVisibleProperty Include=&quot;BanAPIFileName&quot; /&amp;gt;&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;AdditionalFiles Include=&quot;BanList.txt&quot; /&amp;gt;&lt;/code&gt; 这两个配置内容，这两个配置内容已经被分析器 NuGet 包的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Package.targets&lt;/code&gt; 文件提供了。整个项目看起来更加简洁&lt;/p&gt;

&lt;p&gt;以上在分析器 NuGet 包里面存放 props 和 targets 的全部项目代码放在 &lt;a href=&quot;https://github.com/lindexi/lindexi_gd/tree/fb40665eacad9578d14bf799969bb0e9ac6f0b89/Roslyn/JabeehuharKekajerlurlaw&quot;&gt;github&lt;/a&gt; 和 &lt;a href=&quot;https://gitee.com/lindexi/lindexi_gd/blob/fb40665eacad9578d14bf799969bb0e9ac6f0b89/Roslyn/JabeehuharKekajerlurlaw&quot;&gt;gitee&lt;/a&gt; 上，可以使用如下命令行拉取代码。我整个代码仓库比较庞大，使用以下命令行可以进行部分拉取，拉取速度比较快&lt;/p&gt;

&lt;p&gt;先创建一个空文件夹，接着使用命令行 cd 命令进入此空文件夹，在命令行里面输入以下代码，即可获取到本文的代码&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;git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin fb40665eacad9578d14bf799969bb0e9ac6f0b89
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上使用的是国内的 gitee 的源，如果 gitee 不能访问，请替换为 github 的源。请在命令行继续输入以下代码，将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码，可以发邮件向我要代码&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;git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin fb40665eacad9578d14bf799969bb0e9ac6f0b89
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;获取代码之后，进入 Roslyn/JabeehuharKekajerlurlaw 文件夹，即可获取到源代码&lt;/p&gt;

&lt;p&gt;以上就是 dotnet 的源代码生成器、分析器的入门介绍，希望能够帮助大家更好的了解源代码生成器、分析器的使用方法。在使用过程中，可能以上介绍的内容还不够满足大家的需求。我将在下文给出一些常用方法，供大家参考&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;a href=&quot;/post/IIncrementalGenerator-%E5%A2%9E%E9%87%8F-Source-Generator-%E7%94%9F%E6%88%90%E4%BB%A3%E7%A0%81%E5%85%A5%E9%97%A8-%E8%AF%BB%E5%8F%96-csproj-%E9%A1%B9%E7%9B%AE%E6%96%87%E4%BB%B6%E7%9A%84%E5%B1%9E%E6%80%A7%E9%85%8D%E7%BD%AE.html&quot;&gt;IIncrementalGenerator 增量 Source Generator 生成代码入门 读取 csproj 项目文件的属性配置&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-%E5%88%86%E6%9E%90%E5%99%A8-%E8%AF%BB%E5%8F%96-csproj-%E9%A1%B9%E7%9B%AE%E6%96%87%E4%BB%B6%E7%9A%84-AdditionalFiles-Item-%E7%9A%84-Metadata-%E9%85%8D%E7%BD%AE.html&quot;&gt;Roslyn 分析器 读取 csproj 项目文件的 AdditionalFiles Item 的 Metadata 配置&lt;/a&gt;
&lt;!-- [Roslyn 分析器 读取 csproj 项目文件的 AdditionalFiles Item 的 Metadata 配置 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18459703 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;获取文件的实际本地路径&quot;&gt;获取文件的实际本地路径&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/Roslyn-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90%E5%99%A8-SourceGenerator-%E8%8E%B7%E5%8F%96%E4%BB%A3%E7%A0%81%E6%96%87%E4%BB%B6%E7%9A%84%E6%9C%AC%E5%9C%B0%E7%BB%9D%E5%AF%B9%E8%B7%AF%E5%BE%84.html&quot;&gt;Roslyn 源代码生成器 SourceGenerator 获取代码文件的本地绝对路径&lt;/a&gt;
&lt;!-- [Roslyn 源代码生成器 SourceGenerator 获取代码文件的本地绝对路径 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18705712 ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;获取引用程序集的所有类型&quot;&gt;获取引用程序集的所有类型&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/IIncrementalGenerator-%E5%A2%9E%E9%87%8F-Source-Generator-%E7%94%9F%E6%88%90%E4%BB%A3%E7%A0%81%E5%85%A5%E9%97%A8-%E8%8E%B7%E5%8F%96%E5%BC%95%E7%94%A8%E7%A8%8B%E5%BA%8F%E9%9B%86%E7%9A%84%E6%89%80%E6%9C%89%E7%B1%BB%E5%9E%8B.html&quot;&gt;IIncrementalGenerator 增量 Source Generator 生成代码入门 获取引用程序集的所有类型&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;判断程序集之间的-internalsvisibleto-关系&quot;&gt;判断程序集之间的 InternalsVisibleTo 关系&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/IIncrementalGenerator-%E5%A2%9E%E9%87%8F-Source-Generator-%E7%94%9F%E6%88%90%E4%BB%A3%E7%A0%81%E5%85%A5%E9%97%A8-%E5%88%A4%E6%96%AD%E7%A8%8B%E5%BA%8F%E9%9B%86%E4%B9%8B%E9%97%B4%E7%9A%84-InternalsVisibleTo-%E5%85%B3%E7%B3%BB.html&quot;&gt;IIncrementalGenerator 增量 Source Generator 生成代码入门 判断程序集之间的 InternalsVisibleTo 关系&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;判断程序集的引用关系&quot;&gt;判断程序集的引用关系&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/IIncrementalGenerator-%E5%A2%9E%E9%87%8F-Source-Generator-%E7%94%9F%E6%88%90%E4%BB%A3%E7%A0%81%E5%85%A5%E9%97%A8-%E5%88%A4%E6%96%AD%E7%A8%8B%E5%BA%8F%E9%9B%86%E7%9A%84%E5%BC%95%E7%94%A8%E5%85%B3%E7%B3%BB.html&quot;&gt;IIncrementalGenerator 增量 Source Generator 生成代码入门 判断程序集的引用关系&lt;/a&gt;
&lt;!-- [IIncrementalGenerator 判断程序集的引用关系 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/17678664.html ) --&gt;&lt;/p&gt;

&lt;h3 id=&quot;获取项目默认命名空间&quot;&gt;获取项目默认命名空间&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/IIncrementalGenerator-%E5%A2%9E%E9%87%8F-Source-Generator-%E7%94%9F%E6%88%90%E4%BB%A3%E7%A0%81%E5%85%A5%E9%97%A8-%E8%8E%B7%E5%8F%96%E9%A1%B9%E7%9B%AE%E9%BB%98%E8%AE%A4%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4.html&quot;&gt;IIncrementalGenerator 增量 Source Generator 生成代码入门 获取项目默认命名空间&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;使用符号判断类型&quot;&gt;使用符号判断类型&lt;/h3&gt;

&lt;p&gt;在上文提到的类型判断方法里面，基本全是通过字符串判断的方式，比如在《分析使用了 CollectionAttribute 特性的分部方法》判断是否返回值类型是 System.Collections.Generic.IEnumerable 泛型类型时，就使用了如下的字符串判断方法&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;returnTypeName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methodSymbolReturnType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToDisplayString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fullNameDisplayFormat&lt;/span&gt;&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;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exceptedReturnTypeName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;global::System.Collections.Generic.IEnumerable&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;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;returnTypeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exceptedReturnTypeName&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;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;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;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;methodSymbolReturnType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TypeArguments&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;c1&quot;&gt;// 预期是 IEnumerable&amp;lt;Func&amp;gt; 这样的类型，在 IEnumerable 里面只有一个泛型参数&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;c1&quot;&gt;// 取出 IEnumerable&amp;lt;Func&amp;lt;IContext, IFoo&amp;gt;&amp;gt; 中的 Func&amp;lt;IContext, IFoo&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;methodSymbolReturnType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TypeArguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;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;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INamedTypeSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;funcTypeSymbol&lt;/span&gt;&lt;span class=&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;字符串判断方法本身可以实现一些模糊判断效果，如采用 StartsWith 等方法进行不精确的判断。接下来将和大家介绍使用符号判断类型的方法，通过符号进行判断，可以实现十分精确的判断逻辑，这就意味着在上面代码里面的多个判断条件都可以合并为一个判断&lt;/p&gt;

&lt;p&gt;先取出 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System.Collections.Generic.IEnumerable&lt;/code&gt; 泛型类型，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;// 使用反引号 (`) 和泛型参数的数量在元数据中指定泛型类型。这就是为什么你在元数据名称中看不到“...IEnumerable&amp;lt;T&amp;gt;”的原因&lt;/span&gt;
 &lt;span class=&quot;c1&quot;&gt;// [适用于 ImmutableArrays 的 Roslyn 分析器和代码感知库 - Microsoft Learn](https://learn.microsoft.com/zh-cn/visualstudio/extensibility/roslyn-analyzers-and-code-aware-library-for-immutablearrays?view=vs-2022 )&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;exceptedReturnTypeName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;System.Collections.Generic.IEnumerable`1&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;language-plaintext highlighter-rouge&quot;&gt;global::&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;GeneratorAttributeSyntaxContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxContext&lt;/span&gt; &lt;span class=&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;INamedTypeSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;symbolOfIEnumerable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SemanticModel&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;GetTypeByMetadataName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exceptedReturnTypeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)!;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;获取类型之后，即可和方法返回类型进行对比判断是否相同。由于这里是两个泛型进行对比，需要获取方法返回值类型的 ConstructedFrom 属性，判断代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;ITypeSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;returnType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methodSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReturnType&lt;/span&gt;&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;// 预期是 IEnumerable&amp;lt;Func&amp;lt;IContext, IFoo&amp;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;returnType&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;INamedTypeSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methodSymbolReturnType&lt;/span&gt;&lt;span class=&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;c1&quot;&gt;// 此基础类型必定存在&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;INamedTypeSymbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;symbolOfIEnumerable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntaxContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SemanticModel&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;GetTypeByMetadataName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exceptedReturnTypeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)!;&lt;/span&gt;

 &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methodSymbolReturnTypeConstructedFrom&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methodSymbolReturnType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConstructedFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

 &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;symbolOfIEnumerable&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;methodSymbolReturnTypeConstructedFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SymbolEqualityComparer&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;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;c1&quot;&gt;// 通过以上的符号判断，以下几个判断都可不需要。如判断类型名、判断是否是泛型类型，判断泛型参数数量。这些全部包括在 System.Collections.Generic.IEnumerable`1 类型里面，只要类型符号判断相同，就等于这些条件满足&lt;/span&gt;
 &lt;span class=&quot;c1&quot;&gt;//var returnTypeName = methodSymbolReturnType.ToDisplayString(fullNameDisplayFormat);&lt;/span&gt;
 &lt;span class=&quot;c1&quot;&gt;//if (!string.Equals(returnTypeName, exceptedReturnTypeName, StringComparison.InvariantCulture))&lt;/span&gt;
 &lt;span class=&quot;c1&quot;&gt;//{&lt;/span&gt;
 &lt;span class=&quot;c1&quot;&gt;//    return null;&lt;/span&gt;
 &lt;span class=&quot;c1&quot;&gt;//}&lt;/span&gt;

 &lt;span class=&quot;c1&quot;&gt;//if (!methodSymbolReturnType.IsGenericType)&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;//    return null;&lt;/span&gt;
 &lt;span class=&quot;c1&quot;&gt;//}&lt;/span&gt;

 &lt;span class=&quot;c1&quot;&gt;//if (methodSymbolReturnType.TypeArguments.Length != 1)&lt;/span&gt;
 &lt;span class=&quot;c1&quot;&gt;//{&lt;/span&gt;
 &lt;span class=&quot;c1&quot;&gt;//    // 预期是 IEnumerable&amp;lt;Func&amp;gt; 这样的类型，在 IEnumerable 里面只有一个泛型参数&lt;/span&gt;
 &lt;span class=&quot;c1&quot;&gt;//    return null;&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;language-plaintext highlighter-rouge&quot;&gt;methodSymbolReturnType&lt;/code&gt; 做相等比较，而是需要取 ConstructedFrom 属性？这是因为 ConstructedFrom 属性是获取把当前“已构造”的泛型类型还原成“去掉自身类型实参”的那个泛型定义的符号，但保留外层已确定的封闭类型实参。这使你可以忽略本类型自己的类型参数替换结果，只比较它到底是哪一个泛型类型模板&lt;/p&gt;

&lt;p&gt;以上代码的 GetTypeByMetadataName 拿到的是“泛型定义”符号（Definition），它不带具体类型实参。而实际代码的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IEnumerable&amp;lt;Func&amp;lt;IContext, IFoo&amp;gt;&amp;gt;&lt;/code&gt; 是一个“已构造”实例。自然两个符号是不相等的。通过 ConstructedFrom 把它还原为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt; 类型符号，两者才语义等价，从而比较成功&lt;/p&gt;

&lt;p&gt;以上代码放在 &lt;a href=&quot;https://github.com/lindexi/lindexi_gd/tree/60954446bebcbc84cfa6e91cb8681b551ac60b93/Roslyn/JicanerekeaFawbairbalbayhearwear&quot;&gt;github&lt;/a&gt; 和 &lt;a href=&quot;https://gitee.com/lindexi/lindexi_gd/tree/60954446bebcbc84cfa6e91cb8681b551ac60b93/Roslyn/JicanerekeaFawbairbalbayhearwear&quot;&gt;gitee&lt;/a&gt; 上，可以使用如下命令行拉取代码。我整个代码仓库比较庞大，使用以下命令行可以进行部分拉取，拉取速度比较快&lt;/p&gt;

&lt;p&gt;先创建一个空文件夹，接着使用命令行 cd 命令进入此空文件夹，在命令行里面输入以下代码，即可获取到本文的代码&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;git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 60954446bebcbc84cfa6e91cb8681b551ac60b93
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上使用的是国内的 gitee 的源，如果 gitee 不能访问，请替换为 github 的源。请在命令行继续输入以下代码，将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码，可以发邮件向我要代码&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;git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 60954446bebcbc84cfa6e91cb8681b551ac60b93
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;获取代码之后，进入 Roslyn/JicanerekeaFawbairbalbayhearwear 文件夹，即可获取到源代码&lt;/p&gt;

&lt;h2 id=&quot;基础知识-1&quot;&gt;基础知识&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/post/Roslyn-%E5%88%86%E6%9E%90%E5%99%A8-INamedTypeSymbol-%E7%9A%84-AllInterfaces-%E5%8C%85%E5%90%AB%E7%B1%BB%E5%9E%8B%E5%85%A8%E9%83%A8%E7%BB%A7%E6%89%BF%E6%8E%A5%E5%8F%A3.html&quot;&gt;Roslyn 分析器 INamedTypeSymbol 的 AllInterfaces 包含类型全部继承接口&lt;/a&gt; &lt;!-- [Roslyn 分析器 INamedTypeSymbol 的 AllInterfaces 包含类型全部继承接口 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18826021 ) --&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/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;li&gt;&lt;a href=&quot;/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;p&gt;&lt;a href=&quot;/post/Roslyn-%E5%A6%82%E4%BD%95%E8%8E%B7%E5%BE%97%E4%B8%80%E4%B8%AA%E7%B1%BB%E7%9A%84%E5%BC%95%E7%94%A8.html&quot;&gt;Roslyn 如何获得一个类的引用&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/post/IIncrementalGenerator-%E5%A2%9E%E9%87%8F-Source-Generator-%E7%94%9F%E6%88%90%E4%BB%A3%E7%A0%81%E5%85%A5%E9%97%A8-%E8%AF%BB%E5%8F%96%E8%A7%A3%E6%9E%90-ValueTuple-%E7%9A%84%E5%AE%9A%E4%B9%89.html&quot;&gt;读取解析 ValueTuple 的定义&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://blog.walterlv.com/post/posts-for-learning-dotnet-build-nuget-roslyn.html&quot;&gt;从零开始学习 dotnet 编译过程和 Roslyn 源码分析 - walterlv&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/post/roslyn.html&quot;&gt;手把手教你写 Roslyn 修改编译&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/dotnet/roslyn/blob/main/docs/features/source-generators.cookbook.md&quot;&gt;Source Generators Cookbook&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/dotnet/roslyn/blob/main/docs/features/source-generators.md&quot;&gt;Source Generators&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.md&quot;&gt;Incremental Generators&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;更多编译器、代码分析、代码生成相关博客，请参阅我的 &lt;a href=&quot;/post/%E5%8D%9A%E5%AE%A2%E5%AF%BC%E8%88%AA.html&quot;&gt;博客导航&lt;/a&gt;&lt;/p&gt;

&lt;!--

[Roslyn 入门：使用 Roslyn 静态分析现有项目中的代码（语法分析） - walterlv](https://blog.walterlv.com/post/analysis-code-of-existed-projects-using-roslyn.html )

IIncrementalGenerator 增量 Source Generator 生成代码入门 从语法到语义 获取类型完全限定名
 --&gt;
</description>
        <pubDate>Tue, 07 Apr 2026 22:01:24 +0000</pubDate>
        <link>/post/dotnet-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90%E5%99%A8%E5%88%86%E6%9E%90%E5%99%A8%E5%85%A5%E9%97%A8.html</link>
        <guid isPermaLink="true">/post/dotnet-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90%E5%99%A8%E5%88%86%E6%9E%90%E5%99%A8%E5%85%A5%E9%97%A8.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>WPF 从裸 Win 32 的 WM_Pointer 消息获取触摸点绘制笔迹</title>
        <description>&lt;p&gt;本文将告诉大家如何在 WPF 里面，接收裸 Win 32 的 WM_Pointer 消息，从消息里面获取触摸点信息，使用触摸点信息绘制简单的笔迹&lt;/p&gt;

&lt;!--more--&gt;

&lt;!-- CreateTime:2024/09/01 07:15:43 --&gt;
&lt;!-- 置顶2 --&gt;
&lt;!-- 发布 --&gt;
&lt;!-- 博客 --&gt;

&lt;p&gt;开始之前必须说明的是使用本文的方法不会带来什么优势，既不能带来笔迹书写上的加速，也不能带来笔迹效果的平滑，且代码复杂。本文唯一的作用只是让大家了解一下基础机制&lt;/p&gt;

&lt;p&gt;需要再次说明的是，在 WPF 里面，开启了 WM_Pointer 消息之后，通过 Touch 或 Stylus 事件收到的信息也是从 WM_Pointer 消息里面过来的。大家可以尝试在 Touch 事件监听函数添加断点，通过堆栈可以看到是从 Windows 消息循环来的&lt;/p&gt;

&lt;p&gt;可以从调用堆栈看到如下函数，此函数就是核心的 WPF 框架里面从 WM_Pointer 消息获取触摸信息的代码&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;&amp;gt;	PresentationCore.dll!System.Windows.Interop.HwndPointerInputProvider.System.Windows.Interop.IStylusInputProvider.FilterMessage(nint hwnd, MS.Internal.Interop.WindowMessage msg, nint wParam, nint lParam, ref bool handled)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个 FilterMessage 函数的大概代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;nint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IStylusInputProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FilterMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nint&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;WindowMessage&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;nint&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;nint&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;n&quot;&gt;handled&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;PointerLogic&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;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;WindowMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WM_ENABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;IsWindowEnabled&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;Win32&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;IntPtrToInt32&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;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;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;WindowMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WM_POINTERENTER&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;nf&quot;&gt;ProcessMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPointerId&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;RawStylusActions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InRange&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;TickCount&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;WindowMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WM_POINTERUPDATE&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;nf&quot;&gt;ProcessMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPointerId&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;RawStylusActions&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;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TickCount&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;WindowMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WM_POINTERDOWN&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;nf&quot;&gt;ProcessMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPointerId&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;RawStylusActions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Down&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;TickCount&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;WindowMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WM_POINTERUP&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;nf&quot;&gt;ProcessMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPointerId&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;RawStylusActions&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;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TickCount&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;WindowMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WM_POINTERLEAVE&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;nf&quot;&gt;ProcessMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPointerId&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;RawStylusActions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OutOfRange&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;TickCount&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;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;由此可以了解到，使用本文自己从 Win32 消息获取的触摸信息，和从 WPF 提供的 Touch 或 Stylus 事件里面获取的触摸信息的来源是相同的&lt;/p&gt;

&lt;p&gt;这时候也许有人会说，在 WPF 里面经过了一些封装，可能性能不如自己写的。我只想说，不要过于自信了哦。且别忘了消息是从 UI 线程里面获取的，无论你用不用 WPF 的事件，在 WPF 底层的解析消息获取触摸数据引发事件的代码都会跑，也就是无论你用不用，需要 WPF 干的活一点都没少。只有一个 UI 线程的情况下，如果用自己解析的，那还会多一点点处理逻辑，完全不如直接使用 WPF 的。再加上 WPF 的解析部分没有多少代码，如果有做性能分析的话，可以看到甚至做路由事件时的命中测试，判断命中到哪个控件和引发事件等逻辑的耗时远比解析来的多。且解析消息的数据耗时接近无法被直接测量出来，即测量所需时间大于解析的性能&lt;/p&gt;

&lt;p&gt;科普就到这里，如果对 WPF 触摸相关感兴趣，请看 &lt;a href=&quot;/post/%E5%8D%9A%E5%AE%A2%E5%AF%BC%E8%88%AA.html&quot;&gt;WPF 触摸相关&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;为了能够在消息里面收到 POINTER 消息，我根据 &lt;a href=&quot;/post/WPF-dotnet-core-%E5%A6%82%E4%BD%95%E5%BC%80%E5%90%AF-Pointer-%E6%B6%88%E6%81%AF%E7%9A%84%E6%94%AF%E6%8C%81.html&quot;&gt;WPF dotnet core 如何开启 Pointer 消息的支持&lt;/a&gt; 博客提供的方法，在 App 构造函数里面添加如下代码开启 Pointer 消息的支持。本文内容里面只给出关键代码片段，如需要全部的项目文件，可到本文末尾找到本文所有代码的下载方法&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;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;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;s&quot;&gt;&quot;Switch.System.Windows.Input.Stylus.EnablePointerSupport&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;a href=&quot;/post/WPF-%E5%A6%82%E4%BD%95%E7%A1%AE%E5%AE%9A%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E5%BC%80%E5%90%AF%E4%BA%86-Pointer-%E8%A7%A6%E6%91%B8%E6%B6%88%E6%81%AF%E7%9A%84%E6%94%AF%E6%8C%81.html&quot;&gt;WPF 如何确定应用程序开启了 Pointer 触摸消息的支持&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;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;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;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;windowInteropHelper&lt;/span&gt; &lt;span class=&quot;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;hwnd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;windowInteropHelper&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;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;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;hwnd&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;k&quot;&gt;unsafe&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;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;再定义上一些消息常量，然后跑起来代码确定 Pointer 消息开启成功&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;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;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WM_POINTERDOWN&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x0246&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_POINTERUPDATE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x0245&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_POINTERUP&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x0247&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;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;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WM_POINTERDOWN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WM_POINTERUPDATE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WM_POINTERUP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
             &lt;span class=&quot;c1&quot;&gt;// 在这里打断点，如果能进断点则证明 Pointer 消息开启成功&lt;/span&gt;
        &lt;span class=&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;以下逻辑需要调用一些 Win32 的 API 函数，为了方便使用，根据 &lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-CsWin32-%E5%BA%93%E7%AE%80%E5%8C%96-Win32-%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8%E9%80%BB%E8%BE%91.html&quot;&gt;dotnet 使用 CsWin32 库简化 Win32 函数调用逻辑&lt;/a&gt; 博客提供的方法，使用 CsWin32 库简化 Win32 函数调用逻辑，可以减少大量的 PInvoke 定义&lt;/p&gt;

&lt;p&gt;可以避免定义错 PInvoke 函数导致的诡异失败&lt;/p&gt;

&lt;p&gt;编辑 csproj 项目文件，替换为如下代码用于快速安装 CsWin32 库&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div 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;net9.0-windows&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Nullable&amp;gt;&lt;/span&gt;enable&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Nullable&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ImplicitUsings&amp;gt;&lt;/span&gt;enable&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ImplicitUsings&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;/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.Windows.CsWin32&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;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.3.106&quot;&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;大家可以看到以上的项目文件代码的 OutputType 被我设置为 exe 类型，如此启动项目将会有默认的控制台，方便我在控制台输出内容&lt;/p&gt;

&lt;p&gt;按照 &lt;a href=&quot;/post/dotnet-%E4%BD%BF%E7%94%A8-CsWin32-%E5%BA%93%E7%AE%80%E5%8C%96-Win32-%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8%E9%80%BB%E8%BE%91.html&quot;&gt;dotnet 使用 CsWin32 库简化 Win32 函数调用逻辑&lt;/a&gt; 博客提供的方法添加 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NativeMethods.txt&lt;/code&gt; 文件，在此文件里面添加一些代码需要用到的 Win32 函数&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;GetPointerTouchInfo
ScreenToClient
RegisterTouchWindow
WM_TOUCH
GetTouchInputInfo
GetPointerDeviceRects
ClientToScreen
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NativeMethods.txt&lt;/code&gt; 文件添加的是所需的 Win32 函数名，添加之后将会由 CsWin32 库使用源代码生成器方式生成对应的 PInvoke 代码和参数所需的类型，如结构体和枚举&lt;/p&gt;

&lt;p&gt;根据 WPF 的源代码，先将消息过来的 wparam 转换为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pointerId&lt;/code&gt; 参数，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;pointerId&lt;/span&gt; &lt;span class=&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;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;wparam&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;n&quot;&gt;PInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPointerTouchInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerId&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里需要额外说明的是这个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pointerId&lt;/code&gt; 参数不等于设备 Id 号，即如 WPF 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TouchDevice.Id&lt;/code&gt; 等，这是不相同的，需要使用 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointercursorid&quot;&gt;GetPointerCursorId&lt;/a&gt; 进行关联才能拿到和 WPF 一样的值。但是使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pointerId&lt;/code&gt; 参数去区分不同的触摸点还是可以的&lt;/p&gt;

&lt;p&gt;如此即可拿到核心的 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-pointer_info&quot;&gt;POINTER_INFO&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;POINTER_INFO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerInfo&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;pointerInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;简单处理的话，拿到的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pointerInfo&lt;/code&gt; 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptPixelLocation&lt;/code&gt; 字段就是当前触摸的坐标点了，采用的是像素坐标，使用屏幕坐标系&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;pointerInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptPixelLocation&lt;/span&gt;&lt;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;div class=&quot;language-csharp highlighter-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;PInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ScreenToClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&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;k&quot;&gt;ref&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;不考虑 DPI 的情况下，这样就可以使用了&lt;/p&gt;

&lt;p&gt;按照 &lt;a href=&quot;/post/WPF-%E6%9C%80%E7%AE%80%E9%80%BB%E8%BE%91%E5%AE%9E%E7%8E%B0%E5%A4%9A%E6%8C%87%E9%A1%BA%E6%BB%91%E7%9A%84%E7%AC%94%E8%BF%B9%E4%B9%A6%E5%86%99.html&quot;&gt;WPF 最简逻辑实现多指顺滑的笔迹书写&lt;/a&gt; 博客提供的方法进行笔迹对接即可绘制出笔迹&lt;/p&gt;

&lt;p&gt;这就是最简单的从 Win32 消息接收 Pointer 消息绘制笔迹的方法&lt;/p&gt;

&lt;p&gt;然而以上的方法也存在不少的问题，比如忽略了 DPI 问题，以及精度问题。在大尺寸触摸屏上，直接使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptPixelLocation&lt;/code&gt; 字段将会画出锯齿的笔迹。如下图，黑色的线是直接使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptPixelLocation&lt;/code&gt; 字段收到的触摸点连接的折线&lt;/p&gt;

&lt;!-- ![](image/WPF 记一个特别简单的点集滤波平滑方法/WPF 记一个特别简单的点集滤波平滑方法2.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi%2F2024830160364905.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;上图红色的曲线是使用 &lt;a href=&quot;/post/WPF-%E8%AE%B0%E4%B8%80%E4%B8%AA%E7%89%B9%E5%88%AB%E7%AE%80%E5%8D%95%E7%9A%84%E7%82%B9%E9%9B%86%E6%BB%A4%E6%B3%A2%E5%B9%B3%E6%BB%91%E6%96%B9%E6%B3%95.html&quot;&gt;WPF 记一个特别简单的点集滤波平滑方法&lt;/a&gt; 博客提供的方法进行平滑的笔迹线
&lt;!-- [WPF 记一个特别简单的点集滤波平滑方法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/18387840 ) --&gt;&lt;/p&gt;

&lt;p&gt;在大屏触摸设备上，从硬件层面就有一层平滑算法了，但是受限于硬件的计算资源，只有简单的平滑。在 Windows 的 WISPTIS 模块里面，也会对触摸做一定的平滑算法，如丢弃某些过于离谱的触摸点。关于 Windows 上的 WISPTIS 模块的平滑算法属于我和系统软件，即软硬件工程师，进行合作测试出来的，他输入的点和我使用 BusHound 抓到得点和 WPF 层报告的点做对比，可以看到硬件层发送过来的点和 BusHound 抓到的相同，而和 WPF 层报告的点大部分情况下相同，只有某些点被丢弃。被丢弃的点是我这边设计的杂点。但是如果报告的触摸点，有瞬间飞到 0,0 点的情况，那这个 0,0 点则不会被丢弃&lt;/p&gt;

&lt;p&gt;在 WPF 层上，从消息到 Touch 事件这里，是不会对点的坐标进行处理，不会执行平滑算法，最多只有做控件坐标转换。在 WPF 的 Ink 模块里面才会对输入的点做更进一步的平滑处理&lt;/p&gt;

&lt;p&gt;我对比了从 Pointer 消息的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptPixelLocation&lt;/code&gt; 字段收到的触摸点对接的 &lt;a href=&quot;/post/WPF-%E6%9C%80%E7%AE%80%E9%80%BB%E8%BE%91%E5%AE%9E%E7%8E%B0%E5%A4%9A%E6%8C%87%E9%A1%BA%E6%BB%91%E7%9A%84%E7%AC%94%E8%BF%B9%E4%B9%A6%E5%86%99.html&quot;&gt;WPF 最简逻辑实现多指顺滑的笔迹书写&lt;/a&gt; 博客提供的方法，和原始博客提供的程序，可以看到还是原来的笔迹更加顺滑&lt;/p&gt;

&lt;p&gt;其核心原因在于 Pointer 消息的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptPixelLocation&lt;/code&gt; 字段拿到的是丢失精度的点，像素为单位。如果在精度稍微高的触摸屏下，将会有明显的锯齿效果&lt;/p&gt;

&lt;p&gt;如果想要获取比较高精度的触摸点，可以使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptHimetricLocationRaw&lt;/code&gt; 字段。这里需要对后缀 Raw 作出更多的说明，在微软&lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-pointer_info&quot;&gt;官方文档&lt;/a&gt;里面说了不带 Raw 的是预测的值，即 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptPixelLocation&lt;/code&gt; 是预测的像素坐标点，而 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptPixelLocationRaw&lt;/code&gt; 是不带预测的像素坐标点。对于咱如果是使用在笔迹上，其实更应该使用的是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptPixelLocationRaw&lt;/code&gt; 是不带预测的像素坐标点。否则预测效果可能会导致毛刺&lt;/p&gt;

&lt;p&gt;使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptHimetricLocationRaw&lt;/code&gt; 字段会稍微复杂，由于 ptHimetricLocationRaw 采用的是 pointerDeviceRect 坐标系，需要转换到屏幕坐标系&lt;/p&gt;

&lt;p&gt;转换方法就是先将 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptHimetricLocationRaw&lt;/code&gt; 的 X 坐标，压缩到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[0-1]&lt;/code&gt; 范围内，然后乘以 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;displayRect&lt;/code&gt; 的宽度，再加上 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;displayRect&lt;/code&gt; 的 left 值，即得到了屏幕坐标系的 X 坐标。压缩到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[0-1]&lt;/code&gt; 范围内的方法就是除以 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pointerDeviceRect&lt;/code&gt; 的宽度。同理可以计算 Y 坐标&lt;/p&gt;

&lt;p&gt;以上的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;displayRect&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pointerDeviceRect&lt;/code&gt; 需要使用 &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerdevicerects&quot;&gt;GetPointerDeviceRects&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;global&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;Win32&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;n&quot;&gt;RECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerDeviceRect&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;global&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;Win32&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;n&quot;&gt;RECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;displayRect&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;PInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPointerDeviceRects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sourceDevice&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;n&quot;&gt;pointerDeviceRect&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;n&quot;&gt;displayRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上代码用到了不安全代码，记得给 Hook 函数标记上 unsafe 作为不安全代码&lt;/p&gt;

&lt;p&gt;根据上文提供的算法，编写如下代码将 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptHimetricLocationRaw&lt;/code&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;// 如果想要获取比较高精度的触摸点，可以使用 ptHimetricLocationRaw 字段&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 由于 ptHimetricLocationRaw 采用的是 pointerDeviceRect 坐标系，需要转换到屏幕坐标系&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 转换方法就是先将 ptHimetricLocationRaw 的 X 坐标，压缩到 [0-1] 范围内，然后乘以 displayRect 的宽度，再加上 displayRect 的 left 值，即得到了屏幕坐标系的 X 坐标。压缩到 [0-1] 范围内的方法就是除以 pointerDeviceRect 的宽度&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 为什么需要加上 displayRect.left 的值？考虑多屏的情况，屏幕可能是副屏&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Y 坐标同理&lt;/span&gt;
           &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;point2D&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;pointerInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptHimetricLocationRaw&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;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerDeviceRect&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;displayRect&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;displayRect&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;pointerInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptHimetricLocationRaw&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;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointerDeviceRect&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;displayRect&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;displayRect&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上代码的 Point2D 类型的定义如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Point2D&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;X&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;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;以上代码获取的是屏幕坐标系的点，需要转换到 WPF 坐标系&lt;/p&gt;

&lt;p&gt;转换过程的两个重点：&lt;/p&gt;

&lt;p&gt;1.底层 ClientToScreen 只支持整数类型，直接转换会丢失精度。即使是 WPF 封装的 PointFromScreen 或 PointToScreen 方法也会丢失精度&lt;/p&gt;

&lt;p&gt;2.需要进行 DPI 换算，必须要求 DPI 感知&lt;/p&gt;

&lt;p&gt;先测量窗口与屏幕的偏移量，这里直接取 0 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;screenTranslate&lt;/span&gt; &lt;span class=&quot;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;PInvoke&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;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&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;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;screenTranslate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;获取当前的 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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dpi&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;GetDpi&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;先做平移，再做 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;n&quot;&gt;point2D&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;point2D&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;screenTranslate&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;point2D&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;screenTranslate&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;point2D&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Point2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;point2D&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;dpi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DpiScaleX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;point2D&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;dpi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DpiScaleY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;此时拿到的 point2D 就是 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;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;WM_POINTERUPDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;strokeVisual&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetStrokeVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pointerId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;strokeVisual&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;StylusPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;point2D&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;point2D&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;strokeVisual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Redraw&lt;/span&gt;&lt;span class=&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;msg&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WM_POINTERUP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;StrokeVisualList&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;pointerId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span 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://github.com/lindexi/lindexi_gd/tree/322313ee55d0eeaae7148b24ca279e1df087871e/WPFDemo/DefilireceHowemdalaqu&quot;&gt;github&lt;/a&gt; 和 &lt;a href=&quot;https://gitee.com/lindexi/lindexi_gd/tree/322313ee55d0eeaae7148b24ca279e1df087871e/WPFDemo/DefilireceHowemdalaqu&quot;&gt;gitee&lt;/a&gt; 上，可以使用如下命令行拉取代码。我整个代码仓库比较庞大，使用以下命令行可以进行部分拉取，拉取速度比较快&lt;/p&gt;

&lt;p&gt;先创建一个空文件夹，接着使用命令行 cd 命令进入此空文件夹，在命令行里面输入以下代码，即可获取到本文的代码&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;git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 322313ee55d0eeaae7148b24ca279e1df087871e
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上使用的是国内的 gitee 的源，如果 gitee 不能访问，请替换为 github 的源。请在命令行继续输入以下代码，将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码，可以发邮件向我要代码&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;git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 322313ee55d0eeaae7148b24ca279e1df087871e
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;获取代码之后，进入 WPFDemo/DefilireceHowemdalaqu 文件夹，即可获取到源代码&lt;/p&gt;

&lt;p&gt;更多 WPF 触摸相关技术博客，请参阅 &lt;a href=&quot;/post/%E5%8D%9A%E5%AE%A2%E5%AF%BC%E8%88%AA.html&quot;&gt;博客导航&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 07 Apr 2026 21:01:54 +0000</pubDate>
        <link>/post/WPF-%E4%BB%8E%E8%A3%B8-Win-32-%E7%9A%84-WM_Pointer-%E6%B6%88%E6%81%AF%E8%8E%B7%E5%8F%96%E8%A7%A6%E6%91%B8%E7%82%B9%E7%BB%98%E5%88%B6%E7%AC%94%E8%BF%B9.html</link>
        <guid isPermaLink="true">/post/WPF-%E4%BB%8E%E8%A3%B8-Win-32-%E7%9A%84-WM_Pointer-%E6%B6%88%E6%81%AF%E8%8E%B7%E5%8F%96%E8%A7%A6%E6%91%B8%E7%82%B9%E7%BB%98%E5%88%B6%E7%AC%94%E8%BF%B9.html</guid>
        
        
        <category>WPF</category>
        
      </item>
    
      <item>
        <title>asp dotnet core 基于 TestServer 做集成测试</title>
        <description>&lt;p&gt;我有一个古老的 dotnet core 3.1 的 asp dotnet core 项目，现在我准备将他升级到 dotnet 5 了。但是我不想和博客园一样翻车，因此我需要做一点集成测试的辅助，尽管依然还是翻车了，但是我要学习博客园伟大的精神，将在这个项目里面所做的所有自动化测试项目的方法写下来&lt;/p&gt;

&lt;!--more--&gt;

&lt;!-- CreateTime:2020/11/24 20:29:54 --&gt;

&lt;p&gt;在开始从 dotnet core 3.1 升级到 dotnet 5 之前，我先开始准备集成测试。一开始准备的测试是开启主机，然后通过网络调用。然而这个方法一开启我就被拖出去了…… 因为开启主机会占用端口，而刚好我的几个项目都采用了相同的端口&lt;/p&gt;

&lt;p&gt;而我开始尝试在配置文件里面指定随机的端口，而此时又有玄学的网络权限，但是我又不知道将谁拖出去&lt;/p&gt;

&lt;p&gt;此时小伙伴给我安利了 TestServer 库，通过这个库可以不监听端口，全部都在内存中跑。当然了，访问外部服务就看注入了，没做注入也依然走网络的。只是自己的应用不会去监听端口而已&lt;/p&gt;

&lt;p&gt;先新建一个项目，这是一个单元测试项目，用来做集成测试&lt;/p&gt;

&lt;p&gt;在 dotnet 里面的套路就是先安装 NuGet 包，然后调用。安装的 NuGet 是 Microsoft.AspNetCore.TestHost 库。这个库一开始需要安装 3.1.10 的版本，在之后项目升级到 dotnet 5 才能使用最新的版本&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div 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.AspNetCore.TestHost&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3.1.10&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;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.NET.Test.Sdk&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;16.8.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.TestAdapter&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;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;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;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;coverlet.collector&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.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;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Moq&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.15.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;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;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;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;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;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSTestEnhancer&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&quot;&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.Text.Encoding.CodePages&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;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.AspNetCore.TestHost&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3.1.10&quot;&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;如果是 .NET 5 的应用，那么 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;net5.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;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;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.NET.Test.Sdk&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;16.10.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.TestAdapter&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2.2.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;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;2.2.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;coverlet.collector&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.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;PrivateAssets&amp;gt;&lt;/span&gt;all&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PrivateAssets&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;IncludeAssets&amp;gt;&lt;/span&gt;runtime; build; native; contentfiles; analyzers; buildtransitive&lt;span class=&quot;nt&quot;&gt;&amp;lt;/IncludeAssets&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;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Moq&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.16.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;MSTestEnhancer&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&quot;&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.AspNetCore.TestHost&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.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;/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;在使用 TestServer 进行集成测试的时候，其实就是将启动主机的逻辑替换掉，如 &lt;a href=&quot;https://www.cnblogs.com/kasnti/p/12246180.html&quot;&gt;ASP.NET Core搭建多层网站架构【12-xUnit单元测试之集成测试】 - kasnti - 博客园&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;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;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestHostBuild&lt;/span&gt;
    &lt;span class=&quot;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;HttpClient&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetTestClient&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;_host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetTestClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AssemblyInitialize&lt;/span&gt;&lt;span class=&quot;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;nf&quot;&gt;GlobalInitialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;testContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;IHost&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;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateAndRun&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;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;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;IHost&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;AssemblyCleanup&lt;/span&gt;&lt;span class=&quot;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;GlobalCleanup&lt;/span&gt;&lt;span class=&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;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;private&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;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IHost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateAndRun&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;CreateHostBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StartAsync&lt;/span&gt;&lt;span class=&quot;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;IHostBuilder&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateHostBuilder&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;Host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateDefaultBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureWebHostDefaults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;webBuilder&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;webBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UseStartup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Startup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&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;UseTestServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//关键是多了这一行建立TestServer&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureAppConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hostingContext&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;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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;appConfigurator&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;nf&quot;&gt;ToAppConfigurator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// 这里使用了 https://github.com/dotnet-campus/dotnetCampus.Configurations 做配置&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;apmConfiguration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;appConfigurator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Of&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApmConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;apmConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DisableApm&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;// 使用 auto fac 代替默认的 IOC 容器 &lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseServiceProviderFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AutofacServiceProviderFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
               
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面代码中的 CreateHostBuilder 和 asp dotnet core 项目的 Program.cs 的代码差不多，只是 ConfigureWebHostDefaults 方法更改了&lt;/p&gt;

&lt;p&gt;而 ConfigureAppConfiguration 是进行配置，这里进行一些测试项目特意的配置，如禁用了我的 APM 服务。在做集成测试的时候，可以选择开启或关闭 APM 服务，如果你的运维小伙伴不会打你，那么还是开始 APM 比较好。这里的代码使用了 &lt;a href=&quot;https://github.com/dotnet-campus/dotnetCampus.Configurations&quot;&gt;https://github.com/dotnet-campus/dotnetCampus.Configurations&lt;/a&gt; 的方法进行配置&lt;/p&gt;

&lt;p&gt;在 MSTest 单元测试项目里面，使用 AssemblyInitialize 特性，可以让某个静态方法在单元测试启动的时候运行一次。而使用 AssemblyCleanup 方法可以在单元测试完成之后，无论是否成功都会调用一次&lt;/p&gt;

&lt;p&gt;因此在 GlobalInitialize 方法标记 AssemblyInitialize 特性，在这里面创建主机然后运行主机。此时运行的主机不会去监听端口，因此不能通过端口的形式去调用他，而是需要使用 TestServer 提供的扩展方法获取 HttpClient 去访问。也就是通过 TestHostBuild.GetTestClient 拿到的才能访问这个在内存的主机&lt;/p&gt;

&lt;p&gt;我对每个控制器都创建一个测试文件，用来进行单元测试&lt;/p&gt;

&lt;p&gt;如我的项目里面有一个 StatusOverviewController 控制器，这个控制器用来返回服务的内容，大概逻辑如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;ApiController&lt;/span&gt;&lt;span class=&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;[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;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;/&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;StatusOverviewController&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;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;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;p&quot;&gt;{&lt;/span&gt;
        	&lt;span class=&quot;c1&quot;&gt;// 也许使用 DateTimeOffset 更清真，但这又不是我写的&lt;/span&gt;
        	&lt;span class=&quot;k&quot;&gt;return&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;yyyy-MM-dd HH:mm:ss.fff&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-csharp highlighter-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;StatusOverviewControllerTest&lt;/span&gt;
    &lt;span class=&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;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetTest&lt;/span&gt;&lt;span class=&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;nn&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;httpClient&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TestHostBuild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetTestClient&lt;/span&gt;&lt;span class=&quot;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;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;httpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetStringAsync&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;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNotNull&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;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;当然了这是对简单的接口可以这样写，但是对复杂的接口来说，有很多特殊的需求，此时就需要用到 CUnit 库了，通过安装 MSTestEnhancer 这个 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;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MSTestEnhancer&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.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;安装之后，就可以写入如下面的逻辑&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;DemoTest&lt;/span&gt;
&lt;span class=&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;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;s&quot;&gt;&quot;当满足 A 条件时，应该发生 A&apos; 事。&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;// Arrange&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Action&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Assert&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
        
        &lt;span class=&quot;s&quot;&gt;&quot;当满足 B 条件时，应该发生 B&apos; 事。&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;// Arrange&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Action&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Assert&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个 CUnit 在 GitHub 上完全开源，请看 &lt;a href=&quot;https://github.com/dotnet-campus/CUnit&quot;&gt;https://github.com/dotnet-campus/CUnit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;在准备好了集成测试项目之后，我就开始准备升级到 dotnet 5 了，然而此时发现构建服务器翻车了，如 &lt;a href=&quot;/post/%E5%88%9A%E5%88%9A%E6%88%91%E4%BB%8E%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%9B%9E%E6%BB%9A%E4%BA%86-dotnet-5-%E7%9A%84%E7%8E%AF%E5%A2%83.html&quot;&gt;刚刚我从服务器回滚了 dotnet 5 的环境&lt;/a&gt; 博客的内容&lt;/p&gt;

&lt;p&gt;终于我通过 &lt;a href=&quot;/post/%E5%A6%82%E4%BD%95%E7%BB%99-CI-CD-%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%90%AD%E5%BB%BA%E4%B8%8A-.NET-5-%E6%9E%84%E5%BB%BA%E5%92%8C%E8%BF%90%E8%A1%8C%E7%8E%AF%E5%A2%83.html&quot;&gt;如何给 CI CD 服务器搭建上 .NET 5 构建和运行环境&lt;/a&gt; 的方法修好了&lt;/p&gt;

&lt;p&gt;然而小伙伴告诉我从 dotnet core 3.1 到 dotnet 5 有如下的更改 &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/core/compatibility/3.1-5.0#core-net-libraries&quot;&gt;Breaking changes, version 3.1 to 5.0 - .NET Core&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;在经过了两天的更新依然失败之后，我强行魔改了代码，上到了 dotet 5 之后，发现了 APM 挂了…… 因 APM 内部使用了原先 dotnet core 3.1 的在 dotnet 5 废弃的接口…… 然后就到了写博客时间了&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;更新，当使用 WebApplicationBuilder 时，应该取 WebApplicationBuilder 的 WebHost 属性调用 UseTestServer 方法，代码如下&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;WebApplicationBuilder&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;WebApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateBuilder&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;WebHost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseTestServer&lt;/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, 07 Apr 2026 03:39:40 +0000</pubDate>
        <link>/post/asp-dotnet-core-%E5%9F%BA%E4%BA%8E-TestServer-%E5%81%9A%E9%9B%86%E6%88%90%E6%B5%8B%E8%AF%95.html</link>
        <guid isPermaLink="true">/post/asp-dotnet-core-%E5%9F%BA%E4%BA%8E-TestServer-%E5%81%9A%E9%9B%86%E6%88%90%E6%B5%8B%E8%AF%95.html</guid>
        
        
        <category>dotnet</category>
        
      </item>
    
      <item>
        <title>实验豆包大模型录音文件识别带上下文识别功能</title>
        <description>&lt;p&gt;本文记录我实验豆包大模型录音文件识别带上下文识别功能&lt;/p&gt;

&lt;!--more--&gt;

&lt;!-- 发布 --&gt;
&lt;!-- 博客 --&gt;

&lt;p&gt;我这里使用的是豆包 ASR 新版控制台，地址是： &lt;a href=&quot;https://console.volcengine.com/speech/new/experience/asr&quot;&gt;https://console.volcengine.com/speech/new/experience/asr&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;使用新版控制台对接比较简单，只需要一个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X-Api-Key&lt;/code&gt; 即可&lt;/p&gt;

&lt;p&gt;进入控制台开通「语音服务」的大模型ASR能力，拿到你的API Key记录起来。我这里存放到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C:\lindexi\Work\Speech.txt&lt;/code&gt; 文件里面&lt;/p&gt;

&lt;!-- ![](image/验豆包大模型录音文件识别带上下文识别功能/验豆包大模型录音文件识别带上下文识别功能0.png) --&gt;
&lt;p&gt;&lt;img src=&quot;http://cdn.lindexi.site/lindexi-2026471012512828.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;目前豆包提供两个版本的录音文件识别模型可选：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;1.0版本：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;volc.bigasr.auc&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;2.0版本：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;volc.seedasr.auc&lt;/code&gt;（本次实验用的是2.0版本，识别准确率更高）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;首先读取API Key，初始化HttpClient并配置公共请求头：&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-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;keyFile&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@&quot;C:\lindexi\Work\Speech.txt&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;key&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;keyFile&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;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;httpClient&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HttpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;httpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DefaultRequestHeaders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryAddWithoutValidation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;X-Api-Key&quot;&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;// 替换成你要使用的模型ID&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resourceId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;volc.seedasr.auc&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;httpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DefaultRequestHeaders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryAddWithoutValidation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;X-Api-Resource-Id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resourceId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requestId&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;n&quot;&gt;httpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DefaultRequestHeaders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryAddWithoutValidation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;X-Api-Request-Id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requestId&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;n&quot;&gt;httpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DefaultRequestHeaders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryAddWithoutValidation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;X-Api-Sequence&quot;&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你需要将上文代码中的文件路径换成你自己的API Key存储路径，也可以直接写配置、用环境变量等方式读取Key，相信这一步难不倒你。&lt;/p&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;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;https://openspeech.bytedance.com/api/v3/auc/bigmodel/submit&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;corpusContext&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CorpusContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ContextData&lt;/span&gt; &lt;span class=&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;CorpusContextData&lt;/span&gt;&lt;span class=&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;你是一个应用软件使用助手，你可以调用工具帮助用户实现电脑的操作&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;nf&quot;&gt;CorpusContextData&lt;/span&gt;&lt;span class=&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;现在电脑上安装的程序有：哔哩哔哩直播姬、软媒魔方、腾讯课堂、微软OfficePLUS、西娃白板、向日葵、小狼毫输入法、QQ影音。请选择将打开的应用程序&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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;corpusContextJson&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JsonSerializer&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;corpusContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JsonSerializerOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;WriteIndented&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;Encoder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JavaScriptEncoder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UnsafeRelaxedJsonEscaping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asrRequest&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AsrRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UserMeta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Uid&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;abc&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Audio&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AudioMeta&lt;/span&gt;&lt;span class=&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;Url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://pro-en-ali-pub.en5static.com/easinote5_public/uwixkwvzhhqjjhnohwvyzzwnykhhihhh.mp3&quot;&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;s&quot;&gt;&quot;mp3&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;Request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RequestMeta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ModelName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;bigmodel&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Corpus&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CorpusMeta&lt;/span&gt;&lt;span class=&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;n&quot;&gt;corpusContextJson&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jsonString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JsonSerializer&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;asrRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JsonSerializerOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Encoder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JavaScriptEncoder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UnsafeRelaxedJsonEscaping&lt;/span&gt;&lt;span class=&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;nn&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;httpResponseMessage&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;httpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PostAsync&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;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jsonString&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;s&quot;&gt;&quot;application/json&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;responseText&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;httpResponseMessage&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;ReadAsStringAsync&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;language-plaintext highlighter-rouge&quot;&gt;CorpusContext&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AsrRequest&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;queryUrl&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://openspeech.bytedance.com/api/v3/auc/bigmodel/query&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;k&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;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;queryHttpResponseMessage&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;httpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PostAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;queryUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringContent&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;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;s&quot;&gt;&quot;application/json&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;queryHttpResponseText&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;queryHttpResponseMessage&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;ReadAsStringAsync&lt;/span&gt;&lt;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;statusCodeText&lt;/span&gt; &lt;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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;queryHttpResponseMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryGetValues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;X-Api-Status-Code&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;statusCode&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;statusCode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&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;n&quot;&gt;Length&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;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;codeArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;statusCodeText&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;codeArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;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;[&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;statusCodeText&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;queryHttpResponseText&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;speechRecognitionResponse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JsonSerializer&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;SpeechRecognitionResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;queryHttpResponseText&lt;/span&gt;&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;IsNullOrEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;speechRecognitionResponse&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;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;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;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;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;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;p&quot;&gt;}&lt;/span&gt;
&lt;span 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;打开西瓜白板录音.mp3: https://pro-en-ali-pub.en5static.com/easinote5_public/uwixjonmhhqjjhnohwvvwyyhvzphihhh.mp3
    &lt;ul&gt;
      &lt;li&gt;会被识别为 “打开西瓜白榜”，但可以提示正为 “西瓜白板”，但就是很难被正确识别为 “希沃白板”&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;打开西wa白板录音.mp3: https://pro-en-ali-pub.en5static.com/easinote5_public/uwixkwvzhhqjjhnohwvyzzwnykhhihhh.mp3
    &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;采用 打开西wa白板录音.mp3 时， &lt;strong&gt;没有加上下文的时候&lt;/strong&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;nl&quot;&gt;&quot;audio_info&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;duration&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4223&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;result&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;additions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;duration&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;4223&quot;&lt;/span&gt;&lt;span class=&quot;p&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;s2&quot;&gt;&quot;打开西瓜白榜。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;utterances&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:[{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;end_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2890&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;start_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1450&lt;/span&gt;&lt;span class=&quot;p&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;s2&quot;&gt;&quot;打开西瓜白榜。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;words&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:[{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;confidence&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;nl&quot;&gt;&quot;end_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1690&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;start_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1450&lt;/span&gt;&lt;span class=&quot;p&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;s2&quot;&gt;&quot;打&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;confidence&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;nl&quot;&gt;&quot;end_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1890&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;start_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1690&lt;/span&gt;&lt;span class=&quot;p&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;s2&quot;&gt;&quot;开&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;confidence&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;nl&quot;&gt;&quot;end_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2170&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;start_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1930&lt;/span&gt;&lt;span class=&quot;p&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;s2&quot;&gt;&quot;西&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;confidence&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;nl&quot;&gt;&quot;end_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2410&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;start_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2170&lt;/span&gt;&lt;span class=&quot;p&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;s2&quot;&gt;&quot;瓜&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;confidence&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;nl&quot;&gt;&quot;end_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2610&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;start_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2570&lt;/span&gt;&lt;span class=&quot;p&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;s2&quot;&gt;&quot;白&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;confidence&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;nl&quot;&gt;&quot;end_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2890&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;start_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2850&lt;/span&gt;&lt;span class=&quot;p&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;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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;加了包含「西娃白板」的上下文之后&lt;/strong&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;mi&quot;&gt;20000000&lt;/span&gt;&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;nl&quot;&gt;&quot;audio_info&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;duration&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3455&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;result&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;additions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;duration&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;3455&quot;&lt;/span&gt;&lt;span class=&quot;p&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;s2&quot;&gt;&quot;打开西娃白板。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;utterances&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:[{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;end_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2470&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;start_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1150&lt;/span&gt;&lt;span class=&quot;p&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;s2&quot;&gt;&quot;打开西娃白板。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;words&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:[{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;confidence&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;nl&quot;&gt;&quot;end_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1390&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;start_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1150&lt;/span&gt;&lt;span class=&quot;p&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;s2&quot;&gt;&quot;打&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;confidence&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;nl&quot;&gt;&quot;end_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1550&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;start_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1390&lt;/span&gt;&lt;span class=&quot;p&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;s2&quot;&gt;&quot;开&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;confidence&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;nl&quot;&gt;&quot;end_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1750&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;start_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1710&lt;/span&gt;&lt;span class=&quot;p&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;s2&quot;&gt;&quot;西&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;confidence&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;nl&quot;&gt;&quot;end_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1950&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;start_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1910&lt;/span&gt;&lt;span class=&quot;p&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;s2&quot;&gt;&quot;娃&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;confidence&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;nl&quot;&gt;&quot;end_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2310&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;start_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2110&lt;/span&gt;&lt;span class=&quot;p&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;s2&quot;&gt;&quot;白&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;confidence&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;nl&quot;&gt;&quot;end_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2470&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;start_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2310&lt;/span&gt;&lt;span class=&quot;p&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;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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&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;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.Text.Encodings.Web&lt;/span&gt;&lt;span class=&quot;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.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;System.Text.Json.Nodes&lt;/span&gt;&lt;span class=&quot;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.Json.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;VolcEngineSdk.OpenSpeech.Contexts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keyFile&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@&quot;C:\lindexi\Work\Speech.txt&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;key&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;keyFile&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;cm&quot;&gt;/*
   豆包录音文件识别模型1.0
   - volc.bigasr.auc
   豆包录音文件识别模型2.0
   - volc.seedasr.auc
 */&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;httpClient&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HttpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;httpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DefaultRequestHeaders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryAddWithoutValidation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;X-Api-Key&quot;&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;resourceId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;volc.seedasr.auc&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;httpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DefaultRequestHeaders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryAddWithoutValidation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;X-Api-Resource-Id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resourceId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requestId&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;n&quot;&gt;httpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DefaultRequestHeaders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryAddWithoutValidation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;X-Api-Request-Id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requestId&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;n&quot;&gt;httpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DefaultRequestHeaders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryAddWithoutValidation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;X-Api-Sequence&quot;&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;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;https://openspeech.bytedance.com/api/v3/auc/bigmodel/submit&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;corpusContext&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CorpusContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ContextData&lt;/span&gt; &lt;span class=&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;CorpusContextData&lt;/span&gt;&lt;span class=&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;你是一个应用软件使用助手，你可以调用工具帮助用户实现电脑的操作&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;nf&quot;&gt;CorpusContextData&lt;/span&gt;&lt;span class=&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;现在电脑上安装的程序有：哔哩哔哩直播姬、软媒魔方、腾讯课堂、微软OfficePLUS、西娃白板、向日葵、小狼毫输入法、QQ影音。请选择将打开的应用程序&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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;corpusContextJson&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JsonSerializer&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;corpusContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JsonSerializerOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;WriteIndented&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;Encoder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JavaScriptEncoder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UnsafeRelaxedJsonEscaping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asrRequest&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AsrRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UserMeta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Uid&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;abc&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Audio&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AudioMeta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// - 打开西瓜白板录音.mp3: https://pro-en-ali-pub.en5static.com/easinote5_public/uwixjonmhhqjjhnohwvvwyyhvzphihhh.mp3 会被识别为 “打开西瓜白榜”，但可以提示正为 “西瓜白板”，但就是很难被正确识别为 “希沃白板”&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// - 打开西wa白板录音.mp3: https://pro-en-ali-pub.en5static.com/easinote5_public/uwixkwvzhhqjjhnohwvyzzwnykhhihhh.mp3 可用提示词 “西娃白板” 或 “西喔白板” 来进行掰歪，但也可以用正确的 “希沃白板”定正&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;https://pro-en-ali-pub.en5static.com/easinote5_public/uwixkwvzhhqjjhnohwvyzzwnykhhihhh.mp3&quot;&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;s&quot;&gt;&quot;mp3&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;Request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RequestMeta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ModelName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;bigmodel&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Corpus&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CorpusMeta&lt;/span&gt;&lt;span class=&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;n&quot;&gt;corpusContextJson&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jsonString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JsonSerializer&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;asrRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JsonSerializerOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Encoder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JavaScriptEncoder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UnsafeRelaxedJsonEscaping&lt;/span&gt;&lt;span class=&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;nn&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;httpResponseMessage&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;httpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PostAsync&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;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jsonString&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;s&quot;&gt;&quot;application/json&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;responseText&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;httpResponseMessage&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;ReadAsStringAsync&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;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;responseText&lt;/span&gt;&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;queryUrl&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://openspeech.bytedance.com/api/v3/auc/bigmodel/query&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;k&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;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;queryHttpResponseMessage&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;httpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PostAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;queryUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringContent&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;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;s&quot;&gt;&quot;application/json&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;queryHttpResponseText&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;queryHttpResponseMessage&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;ReadAsStringAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// {&quot;audio_info&quot;:{&quot;duration&quot;:4223},&quot;result&quot;:{&quot;additions&quot;:{&quot;duration&quot;:&quot;4223&quot;},&quot;text&quot;:&quot;打开西瓜白榜。&quot;,&quot;utterances&quot;:[{&quot;end_time&quot;:2890,&quot;start_time&quot;:1450,&quot;text&quot;:&quot;打开西瓜白榜。&quot;,&quot;words&quot;:[{&quot;confidence&quot;:0,&quot;end_time&quot;:1690,&quot;start_time&quot;:1450,&quot;text&quot;:&quot;打&quot;},{&quot;confidence&quot;:0,&quot;end_time&quot;:1890,&quot;start_time&quot;:1690,&quot;text&quot;:&quot;开&quot;},{&quot;confidence&quot;:0,&quot;end_time&quot;:2170,&quot;start_time&quot;:1930,&quot;text&quot;:&quot;西&quot;},{&quot;confidence&quot;:0,&quot;end_time&quot;:2410,&quot;start_time&quot;:2170,&quot;text&quot;:&quot;瓜&quot;},{&quot;confidence&quot;:0,&quot;end_time&quot;:2610,&quot;start_time&quot;:2570,&quot;text&quot;:&quot;白&quot;},{&quot;confidence&quot;:0,&quot;end_time&quot;:2890,&quot;start_time&quot;:2850,&quot;text&quot;:&quot;榜&quot;}]}]}}&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;statusCodeText&lt;/span&gt; &lt;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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;queryHttpResponseMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryGetValues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;X-Api-Status-Code&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;statusCode&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;statusCode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&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;n&quot;&gt;Length&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;0&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;codeArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;statusCodeText&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;codeArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;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;[&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;statusCodeText&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;queryHttpResponseText&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;speechRecognitionResponse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JsonSerializer&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;SpeechRecognitionResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;queryHttpResponseText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;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;speechRecognitionResponse&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;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;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;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;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;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;p&quot;&gt;}&lt;/span&gt;
&lt;span 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/lindexi/lindexi_gd/tree/0ac18baa912e060d79b4a10a5f77f0c432d954c4/SemanticKernelSamples/ChederehemculerlairLujurraqeldawjear&quot;&gt;github&lt;/a&gt; 和 &lt;a href=&quot;https://gitee.com/lindexi/lindexi_gd/blob/0ac18baa912e060d79b4a10a5f77f0c432d954c4/SemanticKernelSamples/ChederehemculerlairLujurraqeldawjear&quot;&gt;gitee&lt;/a&gt; 上，可以使用如下命令行拉取代码。我整个代码仓库比较庞大，使用以下命令行可以进行部分拉取，拉取速度比较快&lt;/p&gt;

&lt;p&gt;先创建一个空文件夹，接着使用命令行 cd 命令进入此空文件夹，在命令行里面输入以下代码，即可获取到本文的代码&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;git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 0ac18baa912e060d79b4a10a5f77f0c432d954c4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上使用的是国内的 gitee 的源，如果 gitee 不能访问，请替换为 github 的源。请在命令行继续输入以下代码，将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码，可以发邮件向我要代码&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;git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 0ac18baa912e060d79b4a10a5f77f0c432d954c4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;获取代码之后，进入 SemanticKernelSamples/ChederehemculerlairLujurraqeldawjear 文件夹，即可获取到源代码&lt;/p&gt;

&lt;p&gt;更多技术博客，请参阅 &lt;a href=&quot;/post/%E5%8D%9A%E5%AE%A2%E5%AF%BC%E8%88%AA.html&quot;&gt;博客导航&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 07 Apr 2026 02:19:03 +0000</pubDate>
        <link>/post/%E5%AE%9E%E9%AA%8C%E8%B1%86%E5%8C%85%E5%A4%A7%E6%A8%A1%E5%9E%8B%E5%BD%95%E9%9F%B3%E6%96%87%E4%BB%B6%E8%AF%86%E5%88%AB%E5%B8%A6%E4%B8%8A%E4%B8%8B%E6%96%87%E8%AF%86%E5%88%AB%E5%8A%9F%E8%83%BD.html</link>
        <guid isPermaLink="true">/post/%E5%AE%9E%E9%AA%8C%E8%B1%86%E5%8C%85%E5%A4%A7%E6%A8%A1%E5%9E%8B%E5%BD%95%E9%9F%B3%E6%96%87%E4%BB%B6%E8%AF%86%E5%88%AB%E5%B8%A6%E4%B8%8A%E4%B8%8B%E6%96%87%E8%AF%86%E5%88%AB%E5%8A%9F%E8%83%BD.html</guid>
        
        
      </item>
    
      <item>
        <title>Avalonia 已知问题 过早创建 App 对象将抛出 PlatformNotSupportedException 异常</title>
        <description>&lt;p&gt;本文记录 Avalonia 的一个已知问题，过早创建 App 对象将抛出 PlatformNotSupportedException 异常&lt;/p&gt;

&lt;!--more--&gt;

&lt;!-- CreateTime:2026/04/04 07:11:22 --&gt;

&lt;!-- 发布 --&gt;
&lt;!-- 博客 --&gt;

&lt;p&gt;此问题能够在 Avalonia 的 11.3 以及更早版本复现&lt;/p&gt;

&lt;p&gt;只需要让 App 对象在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppBuilder.Configure&lt;/code&gt; 委托之外创建，那么在调用 StartWithClassicDesktopLifetime 方法时，将在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Avalonia.Threading.Dispatcher.MainLoop&lt;/code&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;System.PlatformNotSupportedException: Operation is not supported on this platform.
   at Avalonia.Threading.Dispatcher.MainLoop(CancellationToken cancellationToken)
   at Avalonia.Controls.ApplicationLifetimes.ClassicDesktopStyleApplicationLifetime.StartCore(String[] args)
   at Avalonia.Controls.ApplicationLifetimes.ClassicDesktopStyleApplicationLifetime.Start(String[] args)
   at Avalonia.ClassicDesktopStyleApplicationLifetimeExtensions.StartWithClassicDesktopLifetime(AppBuilder builder, String[] args, Action`1 lifetimeBuilder)
   at RemhemlaidejeheWhahaheenalira.Program.Main(String[] args)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;点进去 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dispatcher.MainLoop&lt;/code&gt; 看，可以发现进入了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_controlledImpl == 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;MainLoop&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;_controlledImpl&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;PlatformNotSupportedException&lt;/span&gt;&lt;span class=&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;cancellationToken&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;k&quot;&gt;delegate&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;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;大概问题原因就是因为 App 过早初始化，导致碰了静态的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dispatcher.UIThread&lt;/code&gt; 属性，进而导致了 Dispatcher 过早初始化，导致 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_controlledImpl&lt;/code&gt; 没有被初始化，这才导致出现此异常&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&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;Dispatcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDispatcher&lt;/span&gt;
&lt;span class=&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;Dispatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UIThread&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MethodImpl&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;AggressiveInlining&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;s_uiThread&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateUIThreadDispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我认为这是 Avalonia 的一个设计问题，但可以按照它现在的框架，也没有什么可以优化&lt;/p&gt;

&lt;p&gt;本文代码放在 &lt;a href=&quot;https://github.com/lindexi/lindexi_gd/tree/266f08d8df93a15051573c88676671d48136aeb0/AvaloniaIDemo/RemhemlaidejeheWhahaheenalira&quot;&gt;github&lt;/a&gt; 和 &lt;a href=&quot;https://gitee.com/lindexi/lindexi_gd/tree/266f08d8df93a15051573c88676671d48136aeb0/AvaloniaIDemo/RemhemlaidejeheWhahaheenalira&quot;&gt;gitee&lt;/a&gt; 上，可以使用如下命令行拉取代码。我整个代码仓库比较庞大，使用以下命令行可以进行部分拉取，拉取速度比较快&lt;/p&gt;

&lt;p&gt;先创建一个空文件夹，接着使用命令行 cd 命令进入此空文件夹，在命令行里面输入以下代码，即可获取到本文的代码&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;git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 266f08d8df93a15051573c88676671d48136aeb0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上使用的是国内的 gitee 的源，如果 gitee 不能访问，请替换为 github 的源。请在命令行继续输入以下代码，将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码，可以发邮件向我要代码&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;git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 266f08d8df93a15051573c88676671d48136aeb0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;获取代码之后，进入 AvaloniaIDemo/RemhemlaidejeheWhahaheenalira 文件夹，即可获取到源代码&lt;/p&gt;

&lt;p&gt;更多技术博客，请参阅 &lt;a href=&quot;/post/%E5%8D%9A%E5%AE%A2%E5%AF%BC%E8%88%AA.html&quot;&gt;博客导航&lt;/a&gt;&lt;/p&gt;

</description>
        <pubDate>Fri, 03 Apr 2026 23:11:22 +0000</pubDate>
        <link>/post/Avalonia-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E8%BF%87%E6%97%A9%E5%88%9B%E5%BB%BA-App-%E5%AF%B9%E8%B1%A1%E5%B0%86%E6%8A%9B%E5%87%BA-PlatformNotSupportedException-%E5%BC%82%E5%B8%B8.html</link>
        <guid isPermaLink="true">/post/Avalonia-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E8%BF%87%E6%97%A9%E5%88%9B%E5%BB%BA-App-%E5%AF%B9%E8%B1%A1%E5%B0%86%E6%8A%9B%E5%87%BA-PlatformNotSupportedException-%E5%BC%82%E5%B8%B8.html</guid>
        
        
        <category>Avalonia</category>
        
      </item>
    
  </channel>
</rss>
