Silverlight5的CoreCLR--老樹開新花


  曾經有一段時間,我糾結於.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 };
View Code

還有一個函數定義:

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 }
View Code

大家可以看到上面的代碼,實驗性質的,注意,加載的托管代碼所在程序集必須加強名稱。

我們得到接口,先進行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放棄了,代碼里真正有用的東西不多。我把磚拋出來了,就等着玉了。

 http://files.cnblogs.com/binsys/CoreCLRTest_201306081354.7z


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM