zoukankan      html  css  js  c++  java
  • 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放弃了,代码里真正有用的东西不多。我把砖抛出来了,就等着玉了。

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

  • 相关阅读:
    ubuntu 16.04下源码安装opencv3.4
    机器学习库--dlib
    ubuntu查看内存占用和查看cpu使用情况的简单方法(ubuntu内存管理)
    语音开放平台简介
    语音开源代码简介
    语音开源代码与平台对比
    source insight 添加 python 支持
    Taglist: Exuberant ctags (http://ctags.sf.net) not found in PATH. Plugin is not loaded
    人脸检测----Adaboost学习方法
    人脸检测---特征的提取
  • 原文地址:https://www.cnblogs.com/binsys/p/3126549.html
Copyright © 2011-2022 走看看