【.Net Framework 體積大?】不安裝.net framework 也能運行!?原理簡介-2(補充)

看完點推薦,推薦數目超過100,打包腳本+工具+運行時 所有分享哈。。。。。(無公司爭議,請放心)html

 

接上一篇前端

【.Net Framework 體積大?】不安裝.net framework 也能運行!?開篇敘述-1node

 

下一篇編程

【.Net Framework 體積大?】不安裝.net framework 也能運行!?原理補充-3小程序

 

  昨天寫了一個引子,仍是有讀者對這套「小把戲」感興趣。那麼不辜負你們的但願,爭取博主不作太監........設計模式

  注意:筆者不想談Link的方式,雖然很爽,可是不靠譜。畢竟解析翻譯到原生的應用,微軟到如今也就敢在Xaml系的應用作嘗試,不知道是微軟的策略,仍是自身都信心不大......api

  至於Mono項目,筆者就不說它了,BUG真的多的一個接一個的,甚至2.0都過去了,3.0尚未修復。雖然它能夠支持Link ,mkbundle 工具,可是bug太多,不當心就是一個雷......服務器

 

  開篇先談下原生的應用和虛機應用。架構

  Native的程序,大多基於特定的平臺硬件系統的,這裏的系統 咱們就說三大主流系統中的Win/Mac/Linux。硬件部分嘛,主要是CPU的類型...原生應用跟隨操做系統的兼容性,能夠相似插入內存條同樣,直接跟系統集成,運行速度快,處理數據速度效率高。開發語言:C/C++  彙編?(呵呵,這都上個世紀的東東)  。基於C/C++ 衍生出來 主流的 QT VC .....VC 裏有衍生出個MFC。。。。。。。app

  虛機應用,這類應用大可能是基於虛機運行時,也就是將運行時 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的兼容性問題。

MSCOREE是託管程序的入口點
讓咱們來作一個小實驗:

首先寫一個最最簡單的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

 
請注意在Vista系統上需提高權限,不然重命名失敗。
以後,運行剛纔編譯出來的EXE程序。Windows直接報錯:

而後,再把mscoree.dll名字改回去,再次運行A.EXE,此次正確打印出了Hello World。

那麼爲何一旦沒有MSCOREE.DLL,就算是最簡單的Hello World也沒法運行呢?

有在Windows用C/C++編程的朋友們應該熟悉上面那個出錯對話框的意思,這個對話框一般在程序找不到所需的DLL的時候出現。咱們能夠經過運行Visual Studio中自帶的Depends.exe來查看A.EXE的對於DLL的依賴關係:

能夠看到A.EXE只對一個DLL有依賴關係,也就是MSCOREE.DLL。而且A.EXE只用到了MSCOREE.DLL中的一個函數,即_CorExeMain。而MSCOREE.DLL自己卻輸出了137個函數之多。從這個函數的名字你們能夠猜出,這個函數是EXE的一個入口點。爲了證明這一點,咱們能夠用DumpBin看看內容:

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)
 
 
注意這個EXE的入口點的RVA是23DE。
再使用Windbg加載A.EXE,使用lmm (list module match)命令查看A.EXE加載的首地址:
 
0:000> lmm a

start    end        module name

00de0000 00de8000   a          (deferred) 

 
 
能夠看到是0x00de0000,那麼再加上23DE這個RVA值,就是程序的入口點。用u命令反彙編看一下:
 
0:000> u de23de
a+0x23de:

00de23de ff250020de00    jmp     dword ptr [a+0x2000 (00de2000)]

 
能夠看到這就是一個簡單的JUMP指令,跳轉到0x00de2000所指向的位置,那麼這個位置是什麼函數呢?咱們能夠經過dds命令查看:
 
0:000> dds 2000+de0000
00de2000 5b034e50 mscoree!_CorExeMain
 
 
到此事實就很是清楚了,任何託管的EXE程序中的入口點都是一條JMP指令直接跳轉到MSCOREE.DLL的_CorExeMain函數執行。而這個_CorExeMain的地址(也就是00de2000所保存的0x5b034e50)則是由OS Loader填入,由於這個位置正是Import Table的位置。
對於一個託管的DLL而言,狀況也很是相似,入口點則是_CorDllMain:

能夠想象的到,假如你的系統中沒有安裝.NET Framework,那麼執行託管程序也會馬上一樣的對話框。顯然根據這個信息用戶自己很難推斷出發生了什麼問題。一個可行的方案是Windows老是自帶一個MSCOREE.DLL,這個MSCOREE.DLL若是發現沒有.NET Framework則會給出比較清晰的報錯信息。不過因爲從Windows 2003以後(包括Vista)的全部Windows版本都會自帶.Net Framework,那麼這個問題基本上不會出現了。

MSCOREE負責選擇.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調用CLR的提供的功能或者定製CLR

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接口。

MSCOREE提供對COM支持

非託管代碼能夠經過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來講,COM只知道CLSID=0029598F-26Fa-46F7-953B-86E2947AB19F}的COM對象位於MSCOREE.DLL中。而事實上,這個類型位於Microsoft.SqlServer.Replication.dll之中,可是COM並不知道,COM只須要知道從MSCOREE.DLL中能夠得到對應的ClassFactory就能夠了,而後COM會經過IClassFactory接口建立這個託管對象的實例,並返回對應的接口。假定一個非託管程序嘗試經過COM建立這樣一個對象,那麼會發生下面這些事情:
1.     程序調用CoCreateInstance通知COM須要建立這樣一個對象,CLSID=0029598F-26Fa-46F7-953B-86E2947AB19F,須要返回IDispatch接口
2.     COM調用CoGetClassObject函數查找對應的入口DLL
3.     COM找到對應的註冊表,發現對應的DLL爲MSCOREE.DLL
4.     COM加載MSCOREE.DLL,調用DllGetClassObject函數,傳入CLSID
5.     MSCOREE.DLL中的DllGetClassObject函數讀入CLSID,找到對應的註冊表,獲取對象的類型名稱,Assembly名稱,CLR版本等信息
6.     DllGetClassObject加載對應版本的CLR
7.     DllGetClassObject返回一個臨時Class Factory對象的IClassFactory接口
8.     COM調用這個Class Factory對象的IClassFactory::CreateInstance方法
9.     這個Class Factory加載對應的CLR,並建立對象,返回一個對象的CCW (Com Callable Wrapper),並該CCW的返回指定的IDispatch接口
能夠看到,這個狀況下起到最關鍵做用的是MSCOREE.DLL所提供的DllGetClassObject函數。

除了對用戶自定義的託管對象提供COM支持以外,MSCOREE.DLL本身也支持少許COM對象,所以MSCOREE.dll支持DllGetClassObject, DllRegisterServer, DllUnregisterServer, DllCanUnloadNow這些COM DLL所須要支持的標準函數。

MSCOREE.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

相關文章
相關標籤/搜索