From 40c05ec6e53f23b934e880b644a8507025dec93a Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Thu, 6 Aug 2020 18:57:18 +0200 Subject: [PATCH] Fix #55847: DOTNET .NET 4.0 GAC new location If we do not specify the exact version of the .NET framework to use, the default CLR is loaded, which is typically CLR 2, which is very old. Therefore, we introduce a `PHP_INI_SYSTEM` setting, which allows users to choose the desired .NET framework version. The value of the setting are the first three parts of the framework's version number, separated by dots, and prefixed with "v", e.g. "v4.0.30319". If the value of the INI setting is `NULL` (the default) or an empty string, the default CLR is used. Internally, we switch from the most generic `CoCreateInstance()` to `CorBindToRuntime()` which is implemented in mscoree.dll. To avoid the hard dependency to that library, we load dynamically. So this fix is supposed to be fully backwards compatible. --- ext/com_dotnet/com_dotnet.c | 44 ++++++++++++++++++++++++++++++---- ext/com_dotnet/com_extension.c | 1 + php.ini-development | 4 ++++ php.ini-production | 4 ++++ 4 files changed, 49 insertions(+), 4 deletions(-) diff --git a/ext/com_dotnet/com_dotnet.c b/ext/com_dotnet/com_dotnet.c index 6bd492ef1d9b9..1b0ba2220c3a0 100644 --- a/ext/com_dotnet/com_dotnet.c +++ b/ext/com_dotnet/com_dotnet.c @@ -117,6 +117,44 @@ struct dotnet_runtime_stuff { DISPID create_instance; }; +/* We link dynamically to mscoree.dll to avoid the hard dependency on .NET + * framework, which is only required if a dotnet instance is to be created. + */ +static HRESULT dotnet_bind_runtime(LPVOID FAR *ppv) +{ + HRESULT hr; + HMODULE mscoree; + typedef HRESULT (STDAPICALLTYPE *cbtr_t)(LPCWSTR pwszVersion, LPCWSTR pwszBuildFlavor, REFCLSID rclsid, REFIID riid, LPVOID FAR *ppv); + cbtr_t CorBindToRuntime; + OLECHAR *oleversion; + char *version; + + mscoree = LoadLibraryA("mscoree.dll"); + if (mscoree == NULL) { + return S_FALSE; + } + + CorBindToRuntime = (cbtr_t) GetProcAddress(mscoree, "CorBindToRuntime"); + if (CorBindToRuntime == NULL) { + FreeLibrary(mscoree); + return S_FALSE; + } + + version = INI_STR("com.dotnet_version"); + if (version == NULL || *version == '\0') { + oleversion = NULL; + } else { + oleversion = php_com_string_to_olestring(version, strlen(version), COMG(code_page)); + } + + hr = CorBindToRuntime(oleversion, NULL, &CLSID_CorRuntimeHost, &IID_ICorRuntimeHost, ppv); + + efree(oleversion); + FreeLibrary(mscoree); + + return hr; +} + static HRESULT dotnet_init(char **p_where) { HRESULT hr; @@ -130,10 +168,8 @@ static HRESULT dotnet_init(char **p_where) } memset(stuff, 0, sizeof(*stuff)); - where = "CoCreateInstance"; - hr = CoCreateInstance(&CLSID_CorRuntimeHost, NULL, CLSCTX_ALL, - &IID_ICorRuntimeHost, (LPVOID*)&stuff->dotnet_host); - + where = "dotnet_bind_runtime"; + hr = dotnet_bind_runtime((LPVOID*)&stuff->dotnet_host); if (FAILED(hr)) goto out; diff --git a/ext/com_dotnet/com_extension.c b/ext/com_dotnet/com_extension.c index 73b70f07ab810..d116f6a5800f8 100644 --- a/ext/com_dotnet/com_extension.c +++ b/ext/com_dotnet/com_extension.c @@ -144,6 +144,7 @@ PHP_INI_BEGIN() STD_PHP_INI_ENTRY("com.autoregister_casesensitive", "1", PHP_INI_ALL, OnAutoregisterCasesensitive, autoreg_case_sensitive, zend_com_dotnet_globals, com_dotnet_globals) STD_PHP_INI_ENTRY("com.code_page", "", PHP_INI_ALL, OnUpdateLong, code_page, zend_com_dotnet_globals, com_dotnet_globals) PHP_INI_ENTRY("com.typelib_file", "", PHP_INI_SYSTEM, OnTypeLibFileUpdate) + PHP_INI_ENTRY("com.dotnet_version", NULL, PHP_INI_SYSTEM, NULL) PHP_INI_END() /* }}} */ diff --git a/php.ini-development b/php.ini-development index ba30258fd0e6a..a7e5fa7f9a1f7 100644 --- a/php.ini-development +++ b/php.ini-development @@ -1614,6 +1614,10 @@ zend.assertions = 1 ; Default: system ANSI code page ;com.code_page= +; The version of the .NET framework to use. The value of the setting are the first three parts +; of the framework's version number, separated by dots, and prefixed with "v", e.g. "v4.0.30319". +;com.dotnet_version= + [mbstring] ; language for internal character representation. ; This affects mb_send_mail() and mbstring.detect_order. diff --git a/php.ini-production b/php.ini-production index a2a22506a404a..0ffb9788ca581 100644 --- a/php.ini-production +++ b/php.ini-production @@ -1618,6 +1618,10 @@ zend.assertions = -1 ; Default: system ANSI code page ;com.code_page= +; The version of the .NET framework to use. The value of the setting are the first three parts +; of the framework's version number, separated by dots, and prefixed with "v", e.g. "v4.0.30319". +;com.dotnet_version= + [mbstring] ; language for internal character representation. ; This affects mb_send_mail() and mbstring.detect_order.