如何读.Net Framework 的源码?

.Net Framework的源码可以从这里下载

Download

也可以在线直接浏览

https://referencesource.microsoft.com

这里我们以System.IO.Directory.CreateDirectory函数为例,来说明如何去读.Net Framework的源码。

在ReferenceSource在线界面的搜索框里输入Directory.CreateDirectory

在列表中选择Directory.CreateDirectory(System.String)重载,在右边可以看到实现的源码

可以看到在函数的最后调用了一个InternalXXX的函数,然后一直往下级找,可以找到Win32Native.CreateDirectory(name,secAttrs)这样一句代码。

这一句代码就是实现创建文件夹的核心,转到Win32Native类,可以看到这里面导入了大量的WinAPI函数。而Win32.CreateDirectory函数实际 上就是调用了Windows API函数CreateDirectory

至此,我们就知道了.Net Framework中创建文件夹实际上还是调用的Windows API函数。

这也就是解释了为什么.Net Framework不能跨平台的原因,因为大部分功能的实现都是借助API函数的,脱离 了Windows系统,这些函数就用不了了。

在.Net Core中,这些API全部被重写了,所以.Net Core能支持跨平台了。

下面我们以一个复杂一点的情况,来说明一下如何查找这些InternalXXX函数的实现。这个分析的过程会比较长,需要你耐心的看完。

这里我们以**System.Threading.Thread.Start()**函数为例,该函数的作用是创建一个线程并启动。在Win32编程里,创建线程是调用的CreateThread API函数。在.Net Framework中,实际上也是调用的这个函数,只是做了多层封装。

referencesource.microsoft.com搜索Thread.Start

在右边的源码中可以看到,在Thread.Start函数的最后调用了一个叫StartInternal的函数,这是一个导出函数,在这里我们看不到这个函数源码。因为这些是在cli中实现的。

我在github上找到一个cli的2.0的源码,其它.Net Framework版本对应的cli代码官方并没有公开。GitHub - SSCLI/sscli20_20060311: Version: 2.0 Date Published: 3/23/2006. The Shared Source CLI is a compressed archive of the source code to a working implementation of the ECMA CLI and the ECMA C# language specification. This implementation builds and runs on Windows XP.。虽然是2.0的,但是并不影响我们深入了解.Net Framework的内部实现。

新版本的.NET平台cli源码是公开的。包括.Net Core 3.1 .NET 5 .NET 6等。GitHub - dotnet/runtime: .NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.

这部分的实现是C++语言,需要一定的C++基础。

我们在shared-source-cli-2.0中搜索StartInternal函数,可以看到这个函数实际 上是调用了ThreadNative的静态函数Start

我们在Core/CLR/vm/comsynchronizable.cpp 中找到ThreadNative类中的 Start函数,源码如下:

复制代码
 1 FCIMPL1(void, ThreadNative::Start, ThreadBaseObject* pThisUNSAFE)
 2 {
 3     ...
 4     StartInner(pThisUNSAFE);
 5     ...
 6 }
 7 
 8 void ThreadNative::StartInner(ThreadBaseObject* pThisUNSAFE)
 9 {
10     ...
11         BOOL success = pNewThread->CreateNewThread(
12                                         pNewThread->RequestedThreadStackSize() /* 0 stackSize override*/,
13                                         KickOffThread, share, nativeThreadName);
14    ...
15 }

可以看到这里实际 上是调用了Thread 类的成员函数CreateNewThread 来创建线程,我们在clr/src/vm/threads.cpp 中找到CreateNewThread函数。

复制代码
 1 BOOL Thread::CreateNewThread(SIZE_T stackSize, LPTHREAD_START_ROUTINE start, void *args)
 2 {
 3    ...
 4     if (!CLRTaskHosted()) {
 5         bRet = CreateNewOSThread(stackSize, start, args);
 6     }
 7     else {
 8         bRet = CreateNewHostTask(stackSize, start, args);
 9     }
10 
11     ...
12 }

CreateNewThread 里面有一个分支,判断是调用CreateNewOsThread 还是CreateNewHostTask ,这里我以CreateNewOsThread 为例,在threads.cpp 中找到CreateOsThread函数,如下

复制代码
 1 BOOL Thread::CreateNewOSThread(SIZE_T sizeToCommitOrReserve, LPTHREAD_START_ROUTINE start, void *args)
 2 {
 3     ...
 4     h = ::CreateThread(NULL     /*=SECURITY_ATTRIBUTES*/,
 5                        sizeToCommitOrReserve,
 6                        intermediateThreadProc,
 7                        lpThreadArgs,
 8                        dwCreationFlags,
 9                        &ourId);
10 
11    ...
12 }

在这个函数里可以看到创建线程的核心代码,就是调用CreateThread。

至此,我们也就完整的了解了.Net Framework中**Thread.Start()**函数的整个实现过程。这其中还有多层封装,我这里不做详细介绍,我这里只是告诉大家如何如何从上层封装找到底层调用。有了这个方法,其它 的.Net API都可以去看一下它到底是怎么实现的。

很早以前就用.Net Reflector去看.Net的源码,但是每次碰到内部调用,就没有办法深入了解了,最近也是一次偶然的机会,我知道 了这些内部调用是在cli中实现的,而cli的最新源码也是开源 的,所以才有了这篇文章。

但这里要提一句的是,Windows API内部是如何实现的,这个我没有去了解过,以我现在的能力,估计也理解不了内部的实现逻辑,如果有小伙伴略懂一二,也可以在下面的评论教 教 我。

相关推荐
向宇it5 小时前
【unity小技巧】unity 什么是反射?反射的作用?反射的使用场景?反射的缺点?常用的反射操作?反射常见示例
开发语言·游戏·unity·c#·游戏引擎
九鼎科技-Leo6 小时前
什么是 WPF 中的依赖属性?有什么作用?
windows·c#·.net·wpf
Heaphaestus,RC7 小时前
【Unity3D】获取 GameObject 的完整层级结构
unity·c#
baivfhpwxf20237 小时前
C# 5000 转16进制 字节(激光器串口通讯生成指定格式命令)
开发语言·c#
直裾7 小时前
Scala全文单词统计
开发语言·c#·scala
ZwaterZ9 小时前
vue el-table表格点击某行触发事件&&操作栏点击和row-click冲突问题
前端·vue.js·elementui·c#·vue
dot.Net安全矩阵11 小时前
.NET 通过模块和驱动收集本地EDR的工具
windows·安全·web安全·.net·交互
zls36536511 小时前
.NET开源实时应用监控系统:WatchDog
.net
djk888811 小时前
.net将List<实体1>的数据转到List<实体2>
数据结构·list·.net
ZwaterZ11 小时前
el-table-column自动生成序号&&在序号前插入图标
前端·javascript·c#·vue