【.Net Framework 体积大?】不安装.net framework 也能运行!?原理简介-2(补充)
看完点推荐,推荐数目超过100,打包脚本+工具+运行时 全部分享哈。。。。。(无公司争议,请放心)
接上一篇
【.Net Framework 体积大?】不安装.net framework 也能运行!?开篇叙述-1
下一篇
【.Net Framework 体积大?】不安装.net framework 也能运行!?原理补充-3
昨天写了一个引子,还是有读者对这套“小把戏”感兴趣。那么不辜负大家的希望,争取博主不做太监........
注意:笔者不想谈Link的方式,虽然很爽,但是不靠谱。毕竟解析翻译到原生的应用,微软到现在也就敢在Xaml系的应用做尝试,不知道是微软的策略,还是自身都信心不大......
至于Mono项目,笔者就不说它了,BUG真的多的一个接一个的,甚至2.0都过去了,3.0还没有修复。虽然它可以支持Link ,mkbundle 工具,但是bug太多,不小心就是一个雷......
开篇先谈下原生的应用和虚机应用。
Native的程序,大多基于特定的平台硬件系统的,这里的系统 我们就说三大主流系统中的Win/Mac/Linux。硬件部分嘛,主要是CPU的类型...原生应用跟随操作系统的兼容性,可以类似插入内存条一样,直接跟系统集成,运行速度快,处理数据速度效率高。开发语言:C/C++ 汇编?(呵呵,这都上个世纪的东东) 。基于C/C++ 衍生出来 主流的 QT VC .....VC 里有衍生出个MFC。。。。。。。
虚机应用,这类应用大多是基于虚机运行时,也就是将运行时 Run time SDK 安装到适配的操作系统后,SDK将硬件 操作系统的不同 进行了自适应。应用开发者,只需要关注程序的运行效果。就好像坑坑哇哇的石头墙,摸了一层水泥,然后上面可以搭载各种形状的石头,而不是特定形状的石头(去适应石头缝大小形状)。特点是:平台适应强 开发速度快 迭代周期缩短...。开发语言:Python Java .net Framework node.js .......
虚机应用,又各自有各自的特定。有的是基于脚本执行,有的是强类型的编译执行。不过大同小异。都离不开运行时!!!
其他语言的运行时,我们不谈,有兴趣,自己研究。我们专门谈一下.net 的运行时 .net Framework.
特点:微软开发,大品牌。体积大,不能说大,简直是巨大!前面说了,从2.0 的几十兆 到3.5的几百兆 到4.0又回到几十兆(但是安装速度真慢的要命,因为要先自解压)。前面说了,十分佩服微软的 cab 压缩,愣是把一个几百兆的.net 4.0 压缩到了几十兆!!!4.5 4.6也都是4系列的。主版本号不变,改变次版本号,说明只是基于4的补丁,但是要亲命的是 4.5 抛弃了XP!!!!XP 啊,提到XP 就好像前端开发者对着IE6那个时代!!! 虽然痛苦恐怖,但是,XP在国内的使用群体依然大的离谱。
虽然 Win7普及的效果不错,但是那毕竟是XP 啊 XP 啊 ............泪奔。
所以,笔者认为 .net 4到目前为止 是兼容最全面的Win 系列的系统。对于开发者来说 4版本无疑是一个划时代的东东,各种应用框架都有,而且性能比3.5好,而且体积小了好多。但是仅仅是相对小了。笔者认为,它依然巨大。相比较 Python Ruby Node.js Lua的运行时,都小巧,安装速度快,运行速度也可以。凭啥.net 这个鬼 体积那么大!??
其实是有原因的。如图:
C:\WINDOWS\Microsoft.NET
这个基本就是.net 的运行时集中营了(虽然在System目录也打入了其他的dll,后面说)
看到这里,我们就需要回忆下,.net 的自身的架构
2.0
3.5
4.0
上图来自:http://www.cnblogs.com/xiaopin/archive/2011/01/07/1929467.html
是不是类似搭积木的方式,一块块的耦合上去的?确实,一个完整的.net framework安装包,需要把整个积木架构搭建起来。但是大多数情况下,我们只需要积木的台子,也就是到 第一个图中的程序集那一层就完事,有GAC 有运行时 ,就足够我们把程序集通过系统引导到 CLR 运行时解析 IL 中间语言到当前平台的原生代码,并执行原生代码。相当于,我们只需要雇佣一个翻译,会翻译其他语言即可,没必要长得高大威猛帅。。。。
那么我们能把上面的枝枝叶叶修建掉么?笔者测试是可以的。
我们只需要把GAC 的相关程序集,还有运行时即可。然后就是特定的引导目录+注册表。
笔者没有找到.net 程序引导的顺序,不过从笔者实验的结果来看。微软打包的程序的时候,给程序集打上了特殊的标识。然后由系统注册表注册的特定应用去解析程序集。类似指定默认程序一样的效果(不知道表述是否正确,欢迎大家指正)。
好,既然是指定的注册表来注册引导程序,那么哪个才是The One? 不卖关子,直接给答案!
注册表:SOFTWARE\Microsoft\.NETFramework\
这个注册表项,有一个安装路径的项,指定到.net的运行时目录。笔者的机器是64位机器,x86的机器就没有64.
然后,注册完路径后,还有它下面的一个注册表键值对:policy
注意其中的键值对:
这个支持策略,类似设计模式中的策略模式。根据特定的场景,使用特定的策略支撑。我们注册4.0后,自然也就支持.net 4.0。
好,注册表关键就这2个,接下来还有关键的一个步骤,在系统目录
一个是箭头指向的dll 还有一个
100那个是4.0的 120那个是笔者安装了 VC 分布包的 VC++2013的。我们需要的是上面的那个哈。。。
好,把这两个程序集 在特定的机器上,也打入进去后,好,我们的精简版本的.net framework就完成了!!
然后,还可以在运行时程序集 目录下,裁剪不需要的程序集 比如什么 exe的小工具啊 WCF 啊 WPF啊什么的。最终就可以粗来一个体积小巧,性能不错,支持.net 4.0的运行时了。
也就是上面的3步走,有兴趣自己尝试,有疑问,请评论留言。笔者不会发布完整的小安装包。版权的问题很蛋痛 呵呵,Congratulations..............
啰嗦一下,为什么添加注册表就能访问策略进行引导,跟.net的历史有关系吧,因为XP系统上面,就已经集成了1.X的.net framework.后续系统Vista win7 server03 08 等等也一样。所以,注册个版本号,也就可以识别粗来了。。。。。
注册表献上:
Root: HKLM; Subkey: "SOFTWARE\Microsoft\.NETFramework";Permissions:admins-full; ValueType: string; ValueName: "InstallRoot"; ValueData: "{win}\Microsoft.NET\Framework\";Check:WebServerRuntimeNotInstall
Root: HKLM; Subkey: "SOFTWARE\Microsoft\.NETFramework\policy\v4.0";Permissions:admins-full;Check:WebServerRuntimeNotInstall
Root: HKLM; Subkey: "SOFTWARE\Microsoft\.NETFramework\policy\v4.0"; Permissions:admins-full; ValueType: string; ValueName: "30319"; ValueData: "30319-30319";Check:WebServerRuntimeHasNotV4SubKey
不要问我,上面是什么鬼,inno setup 又是什么鬼。尽情享用吧.........
---------------------延伸:mscoree.dll-----------------------------
.NET中的幕后英雄:MSCOREE.DLL
现在做.NET Framework的开发的朋友应该是越来越多了,但是可能并非人人都对MSCOREE.DLL非常了解。而事实上,毫不夸张地说,MSCOREE.DLL是.NET Framework中最为核心的DLL之一,没有这个DLL,托管程序根本无法开始执行起来,但是由于这个DLL藏在System32目录下,根本无人问津,可以说是有点委屈了这位.NET Framework中的幕后英雄。本文主要讨论MSCOREE.DLL的几大作用,以及MSCOREE.DLL的兼容性问题。
首先写一个最最简单的Hello World程序,用csc编译(当然你用VS我也没意见):
public class Program
{
public static void Main(string[] args) {
System.Console.WriteLine("Hello World!"); }
}
|
C:/Windows/System32> ren mscoree.dll mscoree_.dll |
然后,再把mscoree.dll名字改回去,再次运行A.EXE,这次正确打印出了Hello World。
那么为什么一旦没有MSCOREE.DLL,就算是最简单的Hello World也无法运行呢?
有在Windows用C/C++编程的朋友们应该熟悉上面那个出错对话框的意思,这个对话框通常在程序找不到所需的DLL的时候出现。我们可以通过运行Visual Studio中自带的Depends.exe来查看A.EXE的对于DLL的依赖关系:
Microsoft (R) COFF/PE Dumper Version 8.00.50727.762 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file a.exe
PE signature found
File Type: EXECUTABLE IMAGE
FILE HEADER VALUES
14C machine (x86)
3 number of sections
46C83E12 time date stamp Sun Aug 19 20:56:50 2007 0 file pointer to symbol table
0 number of symbols
E0 size of optional header
10E characteristics
Executable
Line numbers stripped
Symbols stripped 32 bit word machine
OPTIONAL HEADER VALUES
10B magic # (PE32)
8.00 linker version
400 size of code
600 size of initialized data
0 size of uninitialized data
23DE entry point (004023DE) 2000 base of code
4000 base of data
400000 image base (00400000 to 00407FFF)
|
0:000> lmm a
start end module name 00de0000 00de8000 a (deferred) |
0:000> u de23de
a+0x23de:
00de23de ff250020de00 jmp dword ptr [a+0x2000 (00de2000)] |
0:000> dds 2000+de0000
00de2000 5b034e50 mscoree!_CorExeMain
|
可以想象的到,假如你的系统中没有安装.NET Framework,那么执行托管程序也会立刻同样的对话框。显然根据这个信息用户本身很难推断出发生了什么问题。一个可行的方案是Windows总是自带一个MSCOREE.DLL,这个MSCOREE.DLL如果发现没有.NET Framework则会给出比较清晰的报错信息。不过由于从Windows 2003之后(包括Vista)的所有Windows版本都会自带.Net Framework,那么这个问题基本上不会出现了。
MSCOREE.DLL有个非常特殊的地方,也就是它位于C:/Windows/System32目录下,换句话说,不管你的系统上面安装了多少个不同的.NET Framework版本,这个DLL都最多只可能有2份(32位/64位各一份),而在C:/Windows/Microsoft.NET/Framework 或者C:/Windows/Microsoft.NET/Framework64下面,则会有多份不同的.NET Framework同时存在。那么,这个MSCOREE.DLL又是如何对应不同版本的.NET Framework呢?答案很简单:MSCOREE.DLL通过注册表信息确定系统上面安装的.NET Framework版本号码,然后根据应用程序本身的所要求的版本来选择一个合适的.NET Framework版本来执行。真正的工作则是交给实际的某个版本的 .NET的DLL执行,在通常情况下,这个DLL是Work Station版本的CLR,名为MSCORWKS.DLL,而服务器版本的CLR则对应MSCORSVR.DLL
MSCOREE.DLL导出了大量的函数,那么这些函数是否公开并且可以调用呢?答案是肯定的。几乎所有这些函数在MSDN中都可以查到对应的文档,并且在.NET Framework SDK的Include目录中有一个对应的mscoree.h,提供了这些函数的Prototype。应用程序通过这些函数,可以访问CLR提供的各项功能,比如:
函数名
|
用途
|
GetCORSystemDirectory
|
获得进程中加载的CLR的安装目录
|
GetCORVersion
|
获得进程中加载的CLR的版本西nxi
|
GetFileVersion
|
获得指定文件的CLR版本信息
|
GetRequestedRuntimeInfo
|
获得指定版本CLR的相关信息
|
GetRequestedRuntimeVersion
|
获得应用程序运行所需要的CLR版本信息
|
ClrCreateManagedInstance
|
创建一个.NET对象并返回指定的接口,使用此函数可以访问大量的.NET Framework的已有功能
|
CorBindToRuntime
|
加载指定版本CLR
|
CorBindToRuntimeHost
|
在Host中加载指定版本CLR,Hosting时候使用
|
CreateDebuggingInterfaceFromVersion
|
获得对应版本CLR的ICorDebug接口,用于编写调试器(比如Visual Studio)
|
CorLaunchApplication
|
以指定参数启动托管程序
|
除此之外还有很多,这里只是列了一些比较常用的功能而已。可以看到这些功能都非常有用,特别值得提出的是CorBindToRuntimeHost和CreateDebuggingInterfaceFromVersion。前者提供了对CLR各个方面的定制功能,功能非常强大,有兴趣的朋友可以参考MSDN或者Customizing the Common Language Runtime一书。而后者则提供了对托管程序的调试支持,通过ICorDebug接口。
非托管代码可以通过COM直接调用.NET的Assembly中的托管对象。以下面这个对象为例,该对象CLSID为{0029598F-26Fa-46F7-953B-86E2947AB19F},类型为Microsoft.SqlServer.Replication.ComErrorRecord,线程模型为Both,Assembly名称为Microsoft.SqlServer.Replication, Version=9.0.242.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91,所需CLR版本为v2.0.50727 (2.0 RTM)。最值得注意的是,入口点为mscoree.dll。
除了对用户自定义的托管对象提供COM支持之外,MSCOREE.DLL自己也支持少量COM对象,因此MSCOREE.dll支持DllGetClassObject, DllRegisterServer, DllUnregisterServer, DllCanUnloadNow这些COM DLL所需要支持的标准函数。
我们可以考虑一下,假如我们现在有了.NET Framework 1.0,然后安装了.NET Framework 2.0,那么MSCOREE.DLL会发生变化吗。答案是可能会,如果到2.0到1.0发生了改变需要修改MSCOREE.DLL(比如多加了一个函数),那么肯定要更新MSCOREE.DLL,但是这个MSCOREE.DLL必须完全支持所有.NET Framework 1.0中的MSCOREE.DLL中的函数,否则可以想象所有依赖于这些变化的MSCOREE.DLL中的函数程序都会出错。因此MSCOREE.DLL必须要做到完全向前兼容。
再考虑卸载的情况,假如这个时候.NET Framework 2.0被卸载,那么MSCOREE.DLL会被还原成.NET Framework 1.0的吗?这次答案则是不会。因为2.0的MSCOREE.DLL本身支持.NET Framework 1.0,因此无须替换。这样最为简单。
事实上是,CLR Team一般很少改动MSCOREE.DLL,避免出现兼容性问题,因此可以预见,MSCOREE.DLL将会是.NET Framework/CLR中变化最少的DLL。换句话说,这篇文章的内容,在可以预见的未来几个版本基本上不会过时。OK,对于MSCOREE.DLL的讨论到这里就告一段落,有兴趣的朋友可以自己动手写一些小程序,实际体会一下MSCOREE.DLL所提供的各项功能。
https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/deprecated-clr-hosting-functions
https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/cordllmain-function
_CorDllMain Function
Initializes the common language runtime (CLR), locates the managed entry point in the DLL assembly's CLR header, and begins execution.
Syntax
BOOL STDMETHODCALLTYPE _CorDllMain (
[in] HINSTANCE hInst,
[in] DWORD dwReason,
[in] LPVOID lpReserved
);
Parameters
hInst
[in] The instance handle of the loaded module.
dwReason
[in]Indicates why the DLL entry-point function is being called. This parameter can be one of the following values: DLL_PROCESS_ATTACH, DLL_THREAD_ATTACH, DLL_THREAD_ATTACH, or DLL_PROCESS_DETACH. For descriptions of these values, see the DllMain
documentation in the Platform SDK.
lpReserved
[in] Unused.
Return Value
This method returns true
for success and false
if an error occurs.
Remarks
This function is called by the operating system loader for DLL assemblies. For executable assemblies, the loader calls the _CorExeMain function instead.
The operating system loader calls this method regardless of the entry point specified in the DLL file.
In Windows 98, Windows ME, Windows NT, and Windows 2000, the _CorDllMain
function is called indirectly through a fixupin the operating system loader. In all other versions of Windows, it is called directly by the operating system loader.
For additional information, see the Remarks section in the _CorValidateImage topic.
Requirements
Platforms: See System Requirements.
Header: Cor.h
Library: Included as a resource in MsCorEE.dll
.NET Framework Versions: Available since 1.0
See Also
Metadata Global Static Functions
Feedback
_CorExeMain Function
Initializes the common language runtime (CLR), locates the managed entry point in the executable assembly's CLR header, and begins execution.
Syntax
__int32 STDMETHODCALLTYPE _CorExeMain ();
Remarks
This function is called by the loader in processes created from managed executable assemblies. For DLL assemblies, the loader calls the _CorDllMain function instead.
The operating system loader calls this method regardless of the entry point specified in the image file.
In Windows 98, Windows ME, Windows NT, and Windows 2000, the _CorExeMain
function is called indirectly through a fixup in the operating system loader. In all other versions of Windows, it is called directly by the operating system loader.
For additional information, see the Remarks section in the _CorValidateImage topic.
Requirements
Platforms: See System Requirements.
Header: Cor.h
Library: Included as a resource in MsCorEE.dll
.NET Framework Versions: Available since 1.0
See Also
Metadata Global Static Functions