曾經有一段時間,我糾結於.Net Framework的精簡與XCOPY部署,為此研究了很久應用程序虛擬化,但沒啥成果。曾嘗試過Remotesoft的Linker(就網上廣為流傳的飛信用的那個),還嘗試過Mono精簡,Remotesoft的僅支持.NET 2.0,生成的文件也不小,Mono在Windows上的兼容性貌似還不是很好(當時,非現在),可能跟他們的跨平台戰略有關吧,最后看了SSCLI2.0的代碼,想從那里面精簡出來一份能用的CLR,后來有事兒,就扔下了。
Silverlight我從4開始使用,個人認做類似OA啊,內部的一些系統很好,但微軟因為戰略原因把Silverlight模糊化,導致現在我一提Silverlight就會被說已經被微軟放棄了,不應該再使用,而且一直擔心會不會有SL6。我從接觸Silverlight的那天我就很奇怪,他安裝包才7MB左右,而且國外的啟動研究論壇(貌似叫911cd還是什么)研究它可以直接放在Firefox Portable版里運行,也就是俗稱綠色軟件,不依賴注冊表,不依賴COM組件,根據微軟一篇文章介紹,他和.NET 4的CLR源自同一份代碼,JIT等行為可以說與.NET4的CLR具有一致性,那么他的兼容性是很好的,起碼在Windows平台上,畢竟是微軟自己的,而且他還跨平台支持Mac osx。
Silverlight的核心部分就幾個文件:npctrl.dll,agcore.dll,coreclr.dll。其中npctrl.dll在IE看來他是個ActiveX控件,在Chrome和Firefox看來他是個NP插件。agcore.dll由npctrl.dll加載,是SL的核心部分,實現了繪圖等。coreclr.dll由npctrl.dll,agcore.dll加載,是本文重點核心部分,其就是CoreCLR的實現。
CoreCLR就是那個與.NET4源自同一份代碼的一個精簡CLR,看下 CLR 全面透徹解析 使用 CoreCLR 編寫 Silverlight 和 Silverlight CoreCLR結構淺析 有個大概的了解。自從SL5發布以來,他支持了OOB模式下對WIN32 API的調用,這讓我產生了一些想法,直接用他做.NET 的運行時多好,當然,畢竟精簡過,沒有SYSTEM.WINDOWS.FORM這部分,沒法做UI,但SL本身就是UI啊。帶着這個想法,我到了牆外。。。有了下面的故事。
首先說明,我研究的SL版本是5.1.10411.0,安裝的是X64開發版(里面包含X86版),為什么不用最新的?因為最新版微軟打過安全補丁,導致在微軟的公共符號服務器上炸不到對應的PDB文件(.NET FRAMEWORK4參考源代碼級調試失敗的可以想辦法把.NET4的一些補丁刪除,降到RTM版本,這樣就有了符號以及匹配的參考源代碼),無法使用神器IDA6,這是僅次於最新的版本。我們要研究的是coreclr.dll,拿起IDA加載,等待符號下載完畢,分析完畢,看導出函數,
Name Address Ordinal
---- ------- -------
coreclr_1 79354D14 1
g_CLREngineMetrics 7947E924 2
coreclr_3 791964D7 3
GetCLRRuntimeHost 7919B0B5 4
CoreDllMain(x,x,x) 791963DB
看名稱就知道GetCLRRuntimeHost則個是重點,先把動詞去掉,得到CLRRuntimeHost,放到牆外的google上,我們從MSDN上得到了ICLRRuntimeHost 這個接口,看介紹,是用來對CLR進行宿主的(在非托管程序中加載CLR並使用他加載托管代碼的一種技術),再把動詞Get加上,google下看看有沒有人研究過,然后得到了三篇連載:
http://clrguru.blogspot.com/2009/01/taming-coreclr-part-1.html
http://clrguru.blogspot.com/2009/02/taming-coreclr-part-2.html
http://clrguru.blogspot.com/2009/02/taming-coreclr-concluding.html
(都是牆外的)
他提供了一些思想,但版本已經老了,不適合SL5。
根據我從IDA分析的結果,CLRRuntimeHost這個函數有兩個參數,第一個是傳入IID(GUID),第二個是輸出的接口指針,其實就是直接調用了QueryInterface這個COM技術里的核心函數,第一個IID從IDA的結果來看是 :
EXTERN_GUID(IID_ICLRRuntimeHost2, 0x712AB73F, 0x2C22, 0x4807, 0xAD, 0x7E, 0xF5, 0x1, 0xD7, 0xB7, 0x2C, 0x2D);
既然他叫ICLRRuntimeHost2按照ms的命名習慣,他應該繼承自ICLRRuntimeHost,根據上面的三個短文以及IDA分析我得到了他擴展了4個方法,
CreateAppDomainWithManager
CreateDelegate
Authenticate
UnknowMethod
那么我們根據上面三篇短文以及IDA的結果形成了一個idl文件:
ICLRRuntimeHost2.idl

1 //#define MIDL_PASS 2 //#include "mscoree.h" 3 //import "ocidl.idl"; 4 //import "oleidl.idl"; 5 //import "oaidl.idl"; 6 import "Unknwn.Idl"; 7 8 9 //下面的接口是從sscli2.0 的 mscoree.idl 里面摳出來的 10 11 typedef HRESULT (__stdcall *FExecuteInAppDomainCallback) (void* cookie); 12 13 14 typedef struct _BucketParameters 15 { 16 BOOL fInited; // Set to TRUE if the rest of this structure is valid. 17 WCHAR pszEventTypeName[255]; // Name of the event type. 18 WCHAR pszParams[10][255]; // Parameter strings. 19 } BucketParameters; 20 21 22 23 24 25 // {AD76A023-332D-4298-8001-07AA9350DCA4} 26 cpp_quote("EXTERN_GUID(IID_IPrivateManagedExceptionReporting, 0xAD76A023,0x332D, 0x4298, 0x80, 0x01, 0x07, 0xAA, 0x93, 0x50, 0xDC, 0xA4);") 27 [ 28 uuid(AD76A023-332D-4298-8001-07AA9350DCA4), 29 version(1.0), 30 helpstring("CLR error reporting manager"), 31 pointer_default(unique), 32 local 33 ] 34 interface IPrivateManagedExceptionReporting : IUnknown 35 { 36 // Get Watson bucket parameters for "current" exception (on calling thread). 37 HRESULT GetBucketParametersForCurrentException([out] BucketParameters *pParams); 38 } 39 40 // {02CA073D-7079-4860-880A-C2F7A449C991} 41 cpp_quote("EXTERN_GUID(IID_IHostControl, 0x02CA073C, 0x7079, 0x4860, 0x88, 0x0A, 0xC2, 0xF7, 0xA4, 0x49, 0xC9, 0x91);") 42 [ 43 uuid(02CA073C-7079-4860-880A-C2F7A449C991), 44 version(1.0), 45 helpstring("Common Language Runtime Host Control Interface"), 46 pointer_default(unique), 47 local 48 ] 49 interface IHostControl : IUnknown 50 { 51 HRESULT GetHostManager( 52 [in] REFIID riid, 53 [out] void **ppObject); 54 55 /* Notify Host with IUnknown with the pointer to AppDomainManager */ 56 HRESULT SetAppDomainManager( 57 [in] DWORD dwAppDomainID, 58 [in] IUnknown* pUnkAppDomainManager); 59 } 60 61 62 cpp_quote("EXTERN_GUID(IID_ICLRControl, 0x9065597E, 0xD1A1, 0x4fb2, 0xB6, 0xBA, 0x7E, 0x1F, 0xCE, 0x23, 0x0F, 0x61);") 63 [ 64 uuid(9065597E-D1A1-4fb2-B6BA-7E1FCE230F61), 65 version(1.0), 66 helpstring("Common Language Runtime Control Interface"), 67 pointer_default(unique), 68 local 69 ] 70 interface ICLRControl : IUnknown 71 { 72 HRESULT GetCLRManager( 73 [in] REFIID riid, 74 [out] void **ppObject); 75 76 HRESULT SetAppDomainManagerType( 77 [in] LPCWSTR pwzAppDomainManagerAssembly, 78 [in] LPCWSTR pwzAppDomainManagerType); 79 } 80 81 82 83 84 [ 85 uuid(90F1A06C-7712-4762-86B5-7A5EBA6BDB02), 86 version(1.0), 87 helpstring("Common Language Runtime Hosting Interface"), 88 pointer_default(unique), 89 local 90 ] 91 interface ICLRRuntimeHost : IUnknown 92 { 93 // Starts the runtime. This is equivalent to CoInitializeCor(). 94 HRESULT Start(); 95 96 // Terminates the runtime, This is equivalent CoUninitializeCor(); 97 HRESULT Stop(); 98 99 // Returns an object for configuring runtime, e.g. threading, lock 100 // prior it starts. If the runtime has been initialized this 101 // routine returns an error. See IHostControl. 102 HRESULT SetHostControl([in] IHostControl* pHostControl); 103 104 HRESULT GetCLRControl([out] ICLRControl** pCLRControl); 105 106 HRESULT UnloadAppDomain([in] DWORD dwAppDomainId, 107 [in] BOOL fWaitUntilDone); 108 109 HRESULT ExecuteInAppDomain([in] DWORD dwAppDomainId, 110 [in] FExecuteInAppDomainCallback pCallback, 111 [in] void* cookie); 112 113 HRESULT GetCurrentAppDomainId([out] DWORD *pdwAppDomainId); 114 115 HRESULT ExecuteApplication([in] LPCWSTR pwzAppFullName, 116 [in] DWORD dwManifestPaths, 117 [in] LPCWSTR *ppwzManifestPaths, // optional 118 [in] DWORD dwActivationData, 119 [in] LPCWSTR *ppwzActivationData, // optional 120 [out] int *pReturnValue); 121 122 HRESULT ExecuteInDefaultAppDomain([in] LPCWSTR pwzAssemblyPath, 123 [in] LPCWSTR pwzTypeName, 124 [in] LPCWSTR pwzMethodName, 125 [in] LPCWSTR pwzArgument, 126 [out] DWORD *pReturnValue); 127 }; 128 129 130 //這個接口是根據IDA反匯編找到的 131 cpp_quote("EXTERN_GUID(IID_ICLRRuntimeHost2, 0x712AB73F, 0x2C22, 0x4807, 0xAD, 0x7E, 0xF5, 0x1, 0xD7, 0xB7, 0x2C, 0x2D);") 132 [ 133 uuid(712AB73F-2C22-4807-AD7E-F501D7B72C2D), 134 version(1.0), 135 helpstring("Common Language Runtime Hosting Interface 2"), 136 pointer_default(unique), 137 local 138 ] 139 interface ICLRRuntimeHost2 : ICLRRuntimeHost 140 { 141 HRESULT CreateAppDomainWithManager( 142 [in] LPCWSTR pwzAppDomainName, 143 [in] DWORD appDomainCreateFlags, 144 [in] LPCWSTR pwzManagerAssemblyName, 145 [in] LPCWSTR pwzMAppdomainmanagerName, 146 [in] DWORD appDomainSetupOptionsCount, 147 [in] LPCWSTR* appDomainSetupOptions, 148 [in] LPCWSTR* appDomainSetupValues, 149 [out] DWORD *retAppDomainID); 150 151 HRESULT CreateDelegate( 152 [in] DWORD appDomainID, 153 [in] LPCWSTR assemblyName, 154 [in] LPCWSTR className, 155 [in] LPCWSTR methodName, 156 [out] void *pReturnDelegate); 157 158 //此處應為為了方便分成兩個值 159 //HRESULT Authenticate([in] unsigned __int64); 160 HRESULT Authenticate( 161 [in] DWORD auth1, 162 [in] DWORD auth2); 163 164 165 HRESULT UnknowMethod(); 166 };
還有一個函數定義:
typedef int (__stdcall *GetCLRRuntimeHost)(const IID &CLRGUID,PVOID* ppICLRRuntimeHost2);
ok,加載coreclr的dll,得到ICLRRuntimeHost2,

1 #include "stdafx.h" 2 3 #include <iostream> 4 #include <iomanip> 5 #include <string> 6 #include <locale.h> 7 8 #include "ICLRRuntimeHost2_h.h" 9 10 //#include "ICLRRuntimeHost2.h" 11 12 //#include <mscoree.h> 13 //#include <metahost.h> 14 //#include <corerror.h> 15 16 using namespace std; 17 18 typedef int (__stdcall *GetCLRRuntimeHost)(const IID &CLRGUID,PVOID* ppICLRRuntimeHost2); 19 20 21 //授權1 SL4 及之前版本默認 22 #define AuthCode1_Default 0x94025800 23 24 //授權2 SL5 新增 允許Native Image 25 #define AuthCode1_AllowNativeImage 0x94025801 26 27 //授權2 固定 28 #define AuthCode2 0x01C6CA6F 29 30 31 //FullTrust 完全受信任應用程序域 32 #define AppDomainCreateFlags_FullTrust 0x0000000C 33 34 //Internet Trust Internet域部分受信任應用程序域 35 #define AppDomainCreateFlags_InternetTrust 0x0000000D 36 37 //應用程序域建立標記 38 #define AppDomainCreateFlags AppDomainCreateFlags_FullTrust 39 40 //應用程序域名稱 41 #define AppDomainName L"MyAppDomain" 42 43 //應用程序域Setup選項數量 44 #define AppDomainSetupOptionsCount 4 45 46 //自己實現的 AppDomainManager 總是失敗,難道是coreclr里強制 PublicKeyToken 必須為微軟的 7cec85d7bea7798e ? 47 #define AppDomainManagerASM_MY L"CoreCLRTest.ManagedCode, Version=1.0.0.0, Culture=neutral, PublicKeyToken=48f7de77fe4622c6" 48 #define AppDomainManagerType_MY L"CoreCLRTest.ManagedCode.ApplicationDomainManager" 49 50 //SL5 實現的 AppDomainManager,成功可用 51 #define AppDomainManagerASM_SL5 L"System.Windows.RuntimeHost, Version=5.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" 52 #define AppDomainManagerType_SL5 L"System.Windows.RuntimeHost.HostAppDomainManager" 53 54 //使用SL5的 55 #define AppDomainManagerASM AppDomainManagerASM_SL5 56 #define AppDomainManagerType AppDomainManagerType_SL5 57 58 //要執行的托管代碼靜態方法 所在程序集 59 #define ExecuteStaticDelegate_ASM L"CoreCLRTest.ManagedCode, Version=1.0.0.0, Culture=neutral, PublicKeyToken=48f7de77fe4622c6" 60 61 //要執行的托管代碼靜態方法 所在類型名 62 #define ExecuteStaticDelegate_Type L"CoreCLRTest.ManagedCode.Program" 63 64 //要執行的托管代碼靜態方法 方法名稱 65 #define ExecuteStaticDelegate_MethodName L"Main" 66 67 //方法委托的Native定義 68 typedef void (__stdcall *Main)(); 69 70 71 int _tmain(int argc, _TCHAR* argv[]) 72 { 73 74 //CoreCLR 的 Console 僅支持CodePage65001(UTF-8),但在這里設置並沒有改變控制台窗口的CP,貌似必須用CHCP命令去改變 75 //setlocale(LC_ALL, ".936"); 76 //setlocale(LC_ALL, ".65001"); 77 78 HMODULE clrmodule = LoadLibraryW(L"coreclr.dll"); 79 if(NULL != clrmodule) 80 { 81 GetCLRRuntimeHost pGetCLRRuntimeHost = NULL; 82 pGetCLRRuntimeHost = (GetCLRRuntimeHost)GetProcAddress(clrmodule,"GetCLRRuntimeHost"); 83 84 if(NULL != pGetCLRRuntimeHost) 85 { 86 ICLRRuntimeHost2 *pICLRRuntimeHost2 = NULL; 87 HRESULT rGetCLRRuntimeHost = pGetCLRRuntimeHost(IID_ICLRRuntimeHost2,(PVOID*) &pICLRRuntimeHost2); 88 89 if(S_OK == rGetCLRRuntimeHost) 90 { 91 IPrivateManagedExceptionReporting * pIPrivateManagedExceptionReporting = NULL; 92 93 HRESULT rIPrivateManagedExceptionReporting = pGetCLRRuntimeHost(IID_IPrivateManagedExceptionReporting,(PVOID*) &pIPrivateManagedExceptionReporting); 94 95 96 HRESULT rAuthenticate = pICLRRuntimeHost2->Authenticate(AuthCode1_AllowNativeImage,AuthCode2); 97 if(S_OK == rAuthenticate) 98 { 99 HRESULT rStart = pICLRRuntimeHost2->Start(); //0x04242420 100 if(S_OK == rStart) 101 { 102 LPWSTR path = new WCHAR[MAX_PATH]; 103 if(GetModuleFileNameW(NULL,path,MAX_PATH)) 104 { 105 PathRemoveFileSpecW(path); 106 107 DWORD appDomainID = NULL; 108 LPCWSTR* appDomainSetupOptions = new LPCWSTR[AppDomainSetupOptionsCount]; 109 LPCWSTR* appDomainSetupValues = new LPCWSTR[AppDomainSetupOptionsCount]; 110 111 112 wstring VERSIONING_MANIFEST_BASE (L"default:\""); 113 VERSIONING_MANIFEST_BASE.append(path); 114 VERSIONING_MANIFEST_BASE.append(L"\";arch:\""); 115 VERSIONING_MANIFEST_BASE.append(path); 116 VERSIONING_MANIFEST_BASE.append(L"\""); 117 118 wstring MANIFEST_FILE_PATH (path); 119 MANIFEST_FILE_PATH.append(L"\\slr.dll.managed_manifest"); 120 121 appDomainSetupOptions[0] = L"TRUSTEDPATH"; appDomainSetupValues[0] = path; 122 appDomainSetupOptions[1] = L"VERSIONING_MANIFEST_BASE"; appDomainSetupValues[1] = VERSIONING_MANIFEST_BASE.c_str(); 123 appDomainSetupOptions[2] = L"MANIFEST_FILE_PATH"; appDomainSetupValues[2] = MANIFEST_FILE_PATH.c_str(); 124 appDomainSetupOptions[3] = L"LOADER_OPTIMIZATION"; appDomainSetupValues[3] = L"MultiDomainHost"; 125 //appDomainSetupOptions[4] = L"LOCATION_URI"; appDomainSetupValues[4] = L"file://..C:/Users/Administrator/Desktop/SL/CoreCLRTest/Debug/"; 126 //appDomainSetupOptions[5] = L"PLATFORM_ASSEMBLIES"; appDomainSetupValues[5] = L"CoreCLRTest.ManagedCode;"; 127 //appDomainSetupOptions[6] = L"APPBASE"; appDomainSetupValues[6] = L""; 128 //appDomainSetupOptions[7] = L"PRODUCTID"; appDomainSetupValues[7] = L""; 129 //appDomainSetupOptions[8] = L"MEDIAINSTANCEID"; appDomainSetupValues[8] = L""; 130 //appDomainSetupOptions[9] = L"AppDomainCompatSwitch"; appDomainSetupValues[9] = L""; 131 132 133 134 HRESULT rCreateAppDomainWithManager = pICLRRuntimeHost2->CreateAppDomainWithManager( 135 AppDomainName, 136 AppDomainCreateFlags, 137 AppDomainManagerASM, 138 AppDomainManagerType, 139 AppDomainSetupOptionsCount, 140 appDomainSetupOptions, 141 appDomainSetupValues, 142 &appDomainID); 143 if(S_OK == rCreateAppDomainWithManager) 144 { 145 Main pMain = NULL; 146 147 HRESULT rCreateDelegate = pICLRRuntimeHost2->CreateDelegate( 148 appDomainID, 149 ExecuteStaticDelegate_ASM, 150 ExecuteStaticDelegate_Type, 151 ExecuteStaticDelegate_MethodName, 152 &pMain 153 ); 154 155 if(S_OK == rCreateDelegate) 156 { 157 pMain(); 158 159 HRESULT rUnloadAppDomain = pICLRRuntimeHost2->UnloadAppDomain(appDomainID,TRUE); 160 HRESULT rStop = pICLRRuntimeHost2->Stop(); 161 } 162 else 163 { 164 printf("%s failed!\r\n","CreateDelegate"); 165 } 166 } 167 else 168 { 169 BucketParameters bp; 170 171 pIPrivateManagedExceptionReporting->GetBucketParametersForCurrentException(&bp); 172 173 printf("%s failed!\r\n","CreateAppDomainWithManager"); 174 } 175 176 } 177 else 178 { 179 printf("%s failed!\r\n","GetModuleFileNameW"); 180 } 181 182 183 } 184 else 185 { 186 printf("%s failed!\r\n","Start"); 187 } 188 } 189 else 190 { 191 printf("%s failed!\r\n","Authenticate"); 192 } 193 } 194 else 195 { 196 printf("%s get failed!\r\n","pICLRRuntimeHost2"); 197 } 198 } 199 else 200 { 201 printf("%s get failed!\r\n","pGetCLRRuntimeHost"); 202 } 203 } 204 else 205 { 206 printf("%s load failed!\r\n","coreclr.dll"); 207 } 208 //system("pause"); 209 return 0; 210 }
大家可以看到上面的代碼,實驗性質的,注意,加載的托管代碼所在程序集必須加強名稱。
我們得到接口,先進行Authenticate對host授權驗證(先這么理解着,我也不知道應該怎樣理解),其中AuthCode1_AllowNativeImage是從IDA里找到的,SL5新增的,應該是授權是否允許調用NativeImage的。
然后Start開始引擎,CreateAppDomainWithManager建立應用程序域管理器以及應用程序域,CreateDelegate建立委托並執行他。詳細代碼在文章后面打包。
現在已經實現了控制台輸出輸入,但因為默認控制台編碼是CP936,sl僅支持UNICODE和UTF-8,默認輸出中文會亂碼,這個臨時解決方案是用chcp 65001改變控制台的代碼頁,然后在這個控制台窗口里執行測試程序。現在依舊有的問題是他不加載我自己寫的AppDomainManger,正在研究原因。
CoreCLRTest.ManagedCode這個項目是普通的.NET4類庫項目,然后打開csproj文件進行如下操作:
<!--<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>-->
<!--特別增加開始-->
<TargetFrameworkIdentifier>Silverlight</TargetFrameworkIdentifier>
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
<SilverlightVersion>$(TargetFrameworkVersion)</SilverlightVersion>
<SilverlightApplication>true</SilverlightApplication>
<!--特別增加結束-->
刪除多余的引用,可以發現mscorlib的引用位於sl的目錄,這個就對了。
另外從Windows Live Mesh, Silverlight and the CoreCLR http://www.hanselman.com/blog/WindowsLiveMeshSilverlightAndTheCoreCLR.aspx 這篇文章看,微軟已經偷偷地用這個東西了,雖然微軟關閉了Mesh服務,但大家仍舊可以下載live 2011的套裝安裝包wlsetup-web.exe,從這個安裝程序里安裝mesh,然后到mesh的目錄可以看到他用了coreclr。
至於這個弄出來具體怎么用,那是你們說了算,雖然對沒興趣的人什么也不是,我會繼續研究他的用途(別忘了還有mac上的sl,他可是跨了平台的)。
不要提moonlight了,被mono放棄了,代碼里真正有用的東西不多。我把磚拋出來了,就等着玉了。