聊一聊 C#中有趣的 SourceGenerator生成器

一:背景

1. 讲故事

前些天在看 AOT的时候关注了下 源生成器,挺有意思的一个东西,今天写一篇文章简单的分享下。

二:源生成器探究之旅

1. 源生成器是什么

简单来说,源生成器是Roslyn编译器给程序员开的一道口子,在这个口子里可以塞入一些自定义的cs代码,让Roslyn编译器在编译代码的时候顺带给一起处理了,简单的说就是 夹带私货 ,但古话又说 师不顺路, 医不叩门,所以还是比较尴尬的,看一下官方给的图,图中的橙色区域就是夹带的私货。

有些朋友肯定好奇,这玩意有什么用?其实在AOT领域中,JsonSerializer 就使用了 SourceGeneration 来给序列化的类型(WeatherForecast)生成元数据,参考代码如下:


[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class SourceGenerationContext : JsonSerializerContext
{
}

2. 一个简单的例子

上面的例子不过多深入,先看看怎么实现0到1的问题,这里使用官方例子,用钩子来实现 分布方法 的方法体。

  1. 新建 SourceGenerator 类库项目

这里面的source就是钩子代码,不过目前只能是.NET Standard 2.0项目,应该是要达到最大的兼容性,参考代码如下:


namespace SourceGenerator
{[Generator]public class HelloSourceGenerator : ISourceGenerator{public void Execute(GeneratorExecutionContext context){// Find the main methodvar mainMethod = context.Compilation.GetEntryPoint(context.CancellationToken);// Build up the source codestring source = $@"// <auto-generated/>using System;namespace {mainMethod.ContainingNamespace.ToDisplayString()}{{public static partial class {mainMethod.ContainingType.Name}{{static partial void HelloFrom(string name) =>Console.WriteLine($""Generator says: Hi from '{{name}}'"");}}}}";var typeName = mainMethod.ContainingType.Name;// Add the source code to the compilationcontext.AddSource($"{typeName}.g.cs", source);}public void Initialize(GeneratorInitializationContext context){// No initialization required for this one}}
}
  1. 新建 Example_21_15 项目

这是一个控制台程序,引用刚才的项目,并声明部分方法 HelloFrom,参考代码如下:


namespace Example_21_15
{partial class Program{static void Main(string[] args){HelloFrom("Generated Code");Console.ReadLine();}static partial void HelloFrom(string name);}
}

要记住在 Example_21_15.csproj 中 Include 时要额外增加两个参数,参考如下:

<ItemGroup><ProjectReference Include="..\SourceGenerator\SourceGenerator.csproj"OutputItemType="Analyzer" ReferenceOutputAssembly="false" /></ItemGroup>

配置好之后就可以把程序跑起来了,可以看到方法体确实是钩子中的代码。

二:Roslyn如何夹带私货

1. windbg调试

究竟是如何夹带私货,本质上是 Roslyn 内部的逻辑,现在的问题是如何给他挖出来了呢?这就需要使用强大的 windbg,采用exe启动劫持的方式洞察,流程步骤如下:

  1. windbg 的exe劫持

资深的 WinDbg 玩家我相信都知道这个招数,我写了一个简单的 bat 脚本,对 dotnet.exe 进行启动拦截,要提醒的是有安全软件的话可以先卸载掉,以免出现无权限的问题。


SET ApplicationName=dotnet.exe
SET WinDbgPath=C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exeREG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\%ApplicationName%" /v debugger  /t REG_SZ  /d "%WinDbgPath%" /fECHO 已成功设置PAUSE 

  1. 修改狗子代码

最简单粗暴的方式就是加 Debugger.Break,好让他在 windbg 中自动中断。


public class HelloSourceGenerator : ISourceGenerator
{public void Execute(GeneratorExecutionContext context){Debugger.Break();//...}
}
  1. 使用 dotnet publish 发布程序

所有的埋伏做好之后,最后就是用 dotnet publish 来引诱 Roslyn 出洞,参考命令如下 dotnet publish -r win-x64 -c Debug -o D:\testdump, 命令执行之后果然给拦截到了,截图如下:

由于调用栈难得,再弄一份文字版。


0:029> !clrstack
OS Thread Id: 0x443c (29)Child SP               IP Call Site
00000068E603DD18 00007ffe0135d962 [HelperMethodFrame: 00000068e603dd18] System.Diagnostics.Debugger.BreakInternal()
00000068E603DE20 00007ffd6dd357da System.Diagnostics.Debugger.Break() [/_/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/Debugger.cs @ 18]
00000068E603DE50 00007ffd13920522 SourceGenerator.HelloSourceGenerator.Execute(Microsoft.CodeAnalysis.GeneratorExecutionContext)
00000068E603DF10 00007ffd6a4ad4ac Microsoft.CodeAnalysis.SourceGeneratorAdaptor.b__5_5(Microsoft.CodeAnalysis.SourceProductionContext, GeneratorContextBuilder) [/_/src/Compilers/Core/Portable/SourceGeneration/GeneratorAdaptor.cs @ 55]
00000068E603E020 00007ffd6a5bfdd8 Microsoft.CodeAnalysis.UserFunctionExtensions+c__DisplayClass3_0`2[[Microsoft.CodeAnalysis.SourceProductionContext, Microsoft.CodeAnalysis],[System.__Canon, System.Private.CoreLib]].b__0(Microsoft.CodeAnalysis.SourceProductionContext, System.__Canon, System.Threading.CancellationToken) [/_/src/Compilers/Core/Portable/SourceGeneration/UserFunction.cs @ 101]
00000068E603E070 00007ffd6a624e9c Microsoft.CodeAnalysis.SourceOutputNode`1[[System.__Canon, System.Private.CoreLib]].UpdateStateTable(Builder, Microsoft.CodeAnalysis.NodeStateTable`1,System.Collections.Generic.IEnumerable`1>>, System.Threading.CancellationToken) [/_/src/Compilers/Core/Portable/SourceGeneration/Nodes/SourceOutputNode.cs @ 70]
00000068E603E2B0 00007ffd13fd1ed8 Microsoft.CodeAnalysis.DriverStateTable+Builder.GetLatestStateTableForNode[[System.ValueTuple`2[[System.__Canon, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib]], System.Private.CoreLib]](Microsoft.CodeAnalysis.IIncrementalGeneratorNode`1>) [/_/src/Compilers/Core/Portable/SourceGeneration/Nodes/DriverStateTable.cs @ 60]
00000068E603E320 00007ffd6a625349 Microsoft.CodeAnalysis.SourceOutputNode`1[[System.__Canon, System.Private.CoreLib]].AppendOutputs(Microsoft.CodeAnalysis.IncrementalExecutionContext, System.Threading.CancellationToken) [/_/src/Compilers/Core/Portable/SourceGeneration/Nodes/SourceOutputNode.cs @ 102]
00000068E603E460 00007ffd6a4b0837 Microsoft.CodeAnalysis.GeneratorDriver.UpdateOutputs(System.Collections.Immutable.ImmutableArray`1, Microsoft.CodeAnalysis.IncrementalGeneratorOutputKind, Builder, System.Threading.CancellationToken, Builder) [/_/src/Compilers/Core/Portable/SourceGeneration/GeneratorDriver.cs @ 340]
00000068E603E520 00007ffd6a4afc9b Microsoft.CodeAnalysis.GeneratorDriver.RunGeneratorsCore(Microsoft.CodeAnalysis.Compilation, Microsoft.CodeAnalysis.DiagnosticBag, System.Threading.CancellationToken) [/_/src/Compilers/Core/Portable/SourceGeneration/GeneratorDriver.cs @ 303]
00000068E603ECB0 00007ffd6a4adfc1 Microsoft.CodeAnalysis.GeneratorDriver.RunGeneratorsAndUpdateCompilation(Microsoft.CodeAnalysis.Compilation, Microsoft.CodeAnalysis.Compilation ByRef, System.Collections.Immutable.ImmutableArray`1 ByRef, System.Threading.CancellationToken) [/_/src/Compilers/Core/Portable/SourceGeneration/GeneratorDriver.cs @ 54]
00000068E603EE50 00007ffd6a468763 Microsoft.CodeAnalysis.CommonCompiler.RunGenerators(Microsoft.CodeAnalysis.Compilation, System.String, Microsoft.CodeAnalysis.ParseOptions, System.Collections.Immutable.ImmutableArray`1, Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptionsProvider, System.Collections.Immutable.ImmutableArray`1, Microsoft.CodeAnalysis.DiagnosticBag) [/_/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs @ 838]
00000068E603EF20 00007ffd6a469520 Microsoft.CodeAnalysis.CommonCompiler.CompileAndEmit(Microsoft.CodeAnalysis.TouchedFileLogger, Microsoft.CodeAnalysis.Compilation ByRef, System.Collections.Immutable.ImmutableArray`1, System.Collections.Immutable.ImmutableArray`1, System.Collections.Immutable.ImmutableArray`1, Microsoft.CodeAnalysis.AnalyzerConfigSet, System.Collections.Immutable.ImmutableArray`1, System.Collections.Immutable.ImmutableArray`1, Microsoft.CodeAnalysis.DiagnosticBag, Microsoft.CodeAnalysis.ErrorLogger, System.Threading.CancellationToken, System.Threading.CancellationTokenSource ByRef, Microsoft.CodeAnalysis.Diagnostics.AnalyzerDriver ByRef, System.Nullable`1 ByRef) [/_/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs @ 1145]
00000068E603F280 00007ffd6a468c52 Microsoft.CodeAnalysis.CommonCompiler.RunCore(System.IO.TextWriter, Microsoft.CodeAnalysis.ErrorLogger, System.Threading.CancellationToken) [/_/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs @ 956]
00000068E603F450 00007ffd6a4684b6 Microsoft.CodeAnalysis.CommonCompiler.Run(System.IO.TextWriter, System.Threading.CancellationToken) [/_/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs @ 781]
00000068E603F4C0 00007ffd6aaf9def Microsoft.CodeAnalysis.CompilerServer.CompilerServerHost.RunCompilation(Microsoft.CodeAnalysis.CompilerServer.RunRequest ByRef, System.Threading.CancellationToken) [/_/src/Compilers/Server/VBCSCompiler/CompilerRequestHandler.cs @ 152]
00000068E603F5E0 00007ffd6aaff745 Microsoft.CodeAnalysis.CompilerServer.ClientConnectionHandler+c__DisplayClass8_0.b__1() [/_/src/Compilers/Server/VBCSCompiler/ClientConnectionHandler.cs @ 168]
00000068E603F640 00007ffd6de7b78f System.Threading.Tasks.Task`1[[System.__Canon, System.Private.CoreLib]].InnerInvoke() [/_/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs @ 501]
00000068E603F680 00007ffd6dc364bd System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/ExecutionContext.cs @ 179]
00000068E603F6F0 00007ffd6dc50914 System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef, System.Threading.Thread) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs @ 2345]
00000068E603F9C0 00007ffd72d1c663 [DebuggerU2MCatchHandlerFrame: 00000068e603f9c0] 

最后一个问题就是如果找到这个调用栈上的源码,当然是在 github 上找啦: https://github.com/dotnet/roslyn ,拉下来后就可以根据调用栈上的方法来分析啦,参考如下:

三:总结

在研究底层方面,windbg可谓是一把趁手的兵器,这个例子也算是活生生的论证了一把,否则真的不知道从 Roslyn 何处来论证官方给出的流程图,对吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1551820.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

​​三SSH

ssh密钥对登录原理 &#xff1a;首先&#xff0c;客户端事先生成一对密钥&#xff0c;并将公钥保存在服务器上的授权文件中。接下来&#xff0c;客户端不用密码&#xff0c;而是用密钥对来验证身份。客户端用服务器的公钥来加密自己的公钥&#xff0c;然后把加密后的信息发送给…

面向未来的设计:数字化转型时代的企业架构与建模革新

在数字化转型浪潮席卷全球的今天&#xff0c;企业架构&#xff08;Enterprise Architecture, EA&#xff09;与建模技术正成为引领未来业务发展的核心工具。企业如何设计面向未来的架构&#xff0c;不仅关乎技术的部署&#xff0c;更直接影响业务的战略定位和市场竞争力。《面向…

51单片机的智能停车场【proteus仿真+程序+报告+原理图+演示视频】

1、主要功能 该系统由AT89C51/STC89C52单片机LCD1602显示模块温度传感器DS1302时钟模块红外传感器步进电机按键、蜂鸣器、LED等模块构成。适用于智能停车场车位管理、泊车管理系统等相似项目。 可实现基本功能: 1、LCD1602实时显示北京时间、温度和剩余车位 2、温度传感器DS…

百度百科 X-Bk-Token 算法还原

声明 本文章中所有内容仅供学习交流,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请私信我立即删除! 文章目录 声明案例地址参数分析X-Bk-Token算法追踪X-Bk-Token后缀算法还原c 值跟踪与算法还原往期逆向文章推荐最近太忙了,博客摆烂了好…

Zabbix自动发现SNMP主机

前言 利用Zabbix监控DELL R740主机硬件&#xff0c;监控通过自动发现主机&#xff0c;链接SNMP监控模板 一、配置自动发现 自动发现脚本 cat discovery_host.pyfrom os.path import abspath, dirname, join import json import sysreload(sys) sys.setdefaultencoding(utf-8…

击破壁垒,融合共生,Matter技术点爆智能家居万亿市场!

“爆改”家居生态&#xff0c;点亮万亿蓝海&#xff0c;Matter够格吗&#xff1f; 在万物互联的时代浪潮中&#xff0c;智能家居已加速从单品智能发展至全屋智能&#xff0c;然而之前Apple HomeKit、SamSung SmartThings、Amazon Alexa、Google Home、Aqara Home、TuYa与HomeA…

开放式耳机什么品牌好?精选无差评开放式耳机推荐!

市场上开放式耳机的日益增多为用户带来了丰富的选择&#xff0c;但也使得一些人在众多产品中难以做出决定&#xff0c;不知道开放式耳机哪个牌子的好&#xff1f;为了帮助解决这个问题&#xff0c;我挑选了五款既实用又获得好评的开放式耳机&#xff0c;目的是为大家提供方便&a…

详细指南:如何有效解决Windows系统中msvcp140.dll丢失的解决方法

如果你在使用Windows系统时遇到“msvcp140.dll丢失”的错误提示&#xff0c;通常是因为你的计算机上缺少或损坏了msvcp140.dll文件。msvcp140.dll是Microsoft Visual C Redistributable包的一部分&#xff0c;许多应用程序和游戏需要它来正常运行。以下是几种解决msvcp140.dll丢…

Jetbrains 推出 CodeCanvas:云开发时代的未来已来

人们不大愿意相信事实 只愿意相信故事 你信仰什么 就会怎样生活 近期 jetbrains 悄悄的推出了新的产品 CodeCanvas&#xff0c;这个产品的推出具有划时代的意义。 CodeCanvas 的定位是一个云 IDE 。想一想 jetbrains 从 2000 年开始就专注于 IDE 的开发&#xff0c;准确来说是…

当前用户添加到 [uucp ]组

archlinux使用tabby 查看当前用户&#xff1a;将当前用户添加到 uucp 组验证组成员身份重新登录 /dev/ttyUSB0 设备的所有者是 root&#xff0c;而所属组是 uucp,如果您想以当前用户身份访问此设备&#xff0c;您可以将当前用户添加到 uucp 组中。 以下是将当前用户添加到 uucp…

基于Springboot+Vue的c语言学习辅导网站的设计与实现 (含源码数据库)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 系统中…

HarmonyOS/OpenHarmony Audio 实现音频录制及播放功能

关键词&#xff1a;audio、音频录制、音频播放、权限申请、文件管理 在app的开发过程中时常会遇见一些需要播放一段音频或进行语音录制的场景&#xff0c;那么本期将介绍如何利用鸿蒙 audio 模块实现音频写入和播放的功能。本次依赖的是 ohos.multimedia.audio 音频管理模块&am…

待办事项应用SideQuests

赶在国庆长假前&#xff0c;自驾&#x1f697;出去玩了几天。 国庆前的错峰出游简直是太香了&#xff01;一路上&#x1f6e3;️畅通无阻&#xff0c;停车&#x1f17f;️不用抢&#xff0c;吃饭&#x1f354;不用等&#xff0c;景点&#x1f3de;️不用排队&#xff0c;拍照&…

热门录屏工具详细介绍及上手攻略

如果你的公司业务范围比较广&#xff0c;那应该会频繁进行远程会议吧。对于远程会议最方便的记录方式就是录屏啦。但对于很多人来说&#xff0c;如何选择合适的录屏方法以及使用相关软件可能还存在一些困惑。接下来&#xff0c;就让我们一起深入探讨如何录屏以及了解一些优秀的…

[Notepad++] 文本编辑器的下载及详细安装使用过程(附有下载文件)

程序员常用的文本编辑器Notepad&#xff0c;用于修改配置文件等 下载链接在文末 下载压缩包后解压 &#xff01;&#xff01;安装路径不要有中文 解压文件&#xff0c;得到 双击exe文件 选择简体中文&#xff0c;点击OK 点击下一步 点击“我接受” 更改安装目录&#xff0c;不…

Arthas sm(查看已加载类的方法信息 )

文章目录 二、命令列表2.2 class/classloader相关命令2.2.6 sm&#xff08;查看已加载类的方法信息 &#xff09;举例1&#xff1a;显示类加载的方法举例2&#xff1a;显示类加载的executeTask方法详细信息 本人其他相关文章链接 二、命令列表 2.2 class/classloader相关命令 …

如何使用SCCMSecrets识别SCCM策略中潜在的安全问题

关于SCCMSecrets SCCMSecrets是一款针对SCCM策略的安全扫描与检测工具&#xff0c;该工具旨在提供一种有关 SCCM 策略的全面安全检测方法。 该工具可以从各种权限级别执行&#xff0c;并将尝试发现与策略分发相关的潜在错误配置。除了分发点上托管的包脚本外&#xff0c;它还将…

如何让每一次销售都成为顾客心中的温馨记忆

舒适&#xff0c;乃交往之至高艺术&#xff0c;亦渗透于买卖交易的每一环节。 在这个体验为王的时代&#xff0c;消费者追求的早已超越了物质本身&#xff0c;转而寻觅那份独特的“心灵触动”。他们购买的&#xff0c;实则是一种情感的共鸣&#xff0c;一种被重视与信赖的“感觉…

分糖果C++

题目&#xff1a; 样例解释&#xff1a; 样例1解释 拿 k20 块糖放入篮子里。 篮子里现在糖果数 20≥n7&#xff0c;因此所有小朋友获得一块糖&#xff1b; 篮子里现在糖果数变成 13≥n7&#xff0c;因此所有小朋友获得一块糖&#xff1b; 篮子里现在糖果数变成 6<n7&#xf…

git 报错git: ‘remote-https‘ is not a git command. See ‘git --help‘.

报错内容 原因与解决方案 第一种情况&#xff1a;git路径错误 第一种很好解决&#xff0c;在环境变量中配置正确的git路径即可&#xff1b; 第二种情况 git缺少依赖 这个情况&#xff0c;网上提供了多种解决方案。但如果比较懒&#xff0c;可以直接把仓库地址的https改成ht…