changeset 33557:e9038f9de714 v9.0.2026

patch 9.0.2026: win32: python3 dll loading can be improved Commit: https://github.com/vim/vim/commit/ae3cfa47d3dcee75061db598eb19879693b2393a Author: Ken Takata <kentkt@csc.jp> Date: Sat Oct 14 11:49:09 2023 +0200 patch 9.0.2026: win32: python3 dll loading can be improved Problem: win32: python3 dll loading can be improved Solution: Load DLL from registry path Support loading python3.dll and/or python3xx.dll from the path written in the registry. To support Stable ABI's forwarder DLL (python3.dll), use the `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR` flag for `LoadLibraryExW()` because python3xx.dll is placed in the same directory of python3.dll. If Stable ABI is used, search the latest version from the registry (both from HKEY_CURRENT_USER and HKEY_LOCAL_MACHINE). If Stable ABI is not used, search only the matching version. closes: #13315 Signed-off-by: Christian Brabandt <cb@256bit.org> Co-authored-by: Ken Takata <kentkt@csc.jp>
author Christian Brabandt <cb@256bit.org>
date Sun, 15 Oct 2023 09:13:06 +0200
parents e2f6cf52e496
children 993b7c8565a1
files runtime/doc/if_pyth.txt src/if_python3.c src/version.c
diffstat 3 files changed, 104 insertions(+), 37 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/if_pyth.txt
+++ b/runtime/doc/if_pyth.txt
@@ -754,8 +754,10 @@ you can use Vim without this file.
 MS-Windows ~
 
 To use the Python interface the Python DLL must be in your search path.  In a
-console window type "path" to see what directories are used.  The 'pythondll'
-or 'pythonthreedll' option can be also used to specify the Python DLL.
+console window type "path" to see what directories are used.  If the DLL is
+not found in your search path, Vim will check the registry to find the path
+where Python is installed.  The 'pythondll' or 'pythonthreedll' option can be
+also used to specify the Python DLL.
 
 The name of the DLL should match the Python version Vim was compiled with.
 Currently the name for Python 2 is "python27.dll", that is for Python 2.7.
@@ -782,6 +784,8 @@ and failures.  With Stable ABI, this res
 library with version of at least |v:python3_version| will work.  See
 |has-python| for how to check if Stable ABI is supported, or see if version
 output includes |+python3/dyn-stable|.
+On MS-Windows, 'pythonthreedll' will be set to "python3.dll".  When searching
+the DLL from the registry, Vim will search the latest version of Python.
 
 ==============================================================================
 10. Python 3						*python3*
--- a/src/if_python3.c
+++ b/src/if_python3.c
@@ -835,17 +835,16 @@ Py_ssize_t py3_PyList_GET_SIZE(PyObject 
  * Look up the library "libname" using the InstallPath registry key.
  * Return NULL when failed.  Return an allocated string when successful.
  */
-    static char *
+    static WCHAR *
 py3_get_system_libname(const char *libname)
 {
+    const WCHAR	*pythoncore = L"Software\\Python\\PythonCore";
     const char	*cp = libname;
-    char	subkey[128];
+    WCHAR	subkey[128];
     HKEY	hKey;
-    char	installpath[MAXPATHL];
-    LONG	len = sizeof(installpath);
-    LSTATUS	rc;
-    size_t	sysliblen;
-    char	*syslibname;
+    int		i;
+    DWORD	j, len;
+    LSTATUS	ret;
 
     while (*cp != '\0')
     {
@@ -857,35 +856,95 @@ py3_get_system_libname(const char *libna
 	}
 	++cp;
     }
-    vim_snprintf(subkey, sizeof(subkey),
-#  ifdef _WIN64
-		 "Software\\Python\\PythonCore\\%d.%d\\InstallPath",
-#  else
-		 "Software\\Python\\PythonCore\\%d.%d-32\\InstallPath",
+
+    WCHAR   keyfound[32];
+    HKEY    hKeyTop[] = {HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE};
+    HKEY    hKeyFound = NULL;
+#  ifdef USE_LIMITED_API
+    long    maxminor = -1;
 #  endif
-		 PY_MAJOR_VERSION, PY_MINOR_VERSION);
-    if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, subkey, 0, KEY_QUERY_VALUE, &hKey)
-							      != ERROR_SUCCESS)
-	return NULL;
-    rc = RegQueryValueA(hKey, NULL, installpath, &len);
-    RegCloseKey(hKey);
-    if (ERROR_SUCCESS != rc)
+    for (i = 0; i < ARRAY_LENGTH(hKeyTop); i++)
+    {
+	long	major, minor;
+
+	ret = RegOpenKeyExW(hKeyTop[i], pythoncore, 0, KEY_READ, &hKey);
+	if (ret != ERROR_SUCCESS)
+	    continue;
+	for (j = 0;; j++)
+	{
+	    WCHAR   keyname[32];
+	    WCHAR   *wp;
+
+	    len = ARRAY_LENGTH(keyname);
+	    ret = RegEnumKeyExW(hKey, j, keyname, &len,
+						    NULL, NULL, NULL, NULL);
+	    if (ret == ERROR_NO_MORE_ITEMS)
+		break;
+
+	    major = wcstol(keyname, &wp, 10);
+	    if (*wp == L'.')
+		minor = wcstol(wp + 1, &wp, 10);
+#  ifdef _WIN64
+	    if (*wp != L'\0')
+		continue;
+#  else
+	    if (wcscmp(wp, L"-32") != 0)
+		continue;
+#  endif
+
+	    if (major != PY_MAJOR_VERSION)
+		continue;
+#  ifdef USE_LIMITED_API
+	    // Search the latest version.
+	    if ((minor > maxminor)
+		    && (minor >= ((Py_LIMITED_API >> 16) & 0xff)))
+	    {
+		maxminor = minor;
+		wcscpy(keyfound, keyname);
+		hKeyFound = hKeyTop[i];
+	    }
+#  else
+	    // Check if it matches with the compiled version.
+	    if (minor == PY_MINOR_VERSION)
+	    {
+		wcscpy(keyfound, keyname);
+		hKeyFound = hKeyTop[i];
+		break;
+	    }
+#  endif
+	}
+	RegCloseKey(hKey);
+#  ifdef USE_LIMITED_API
+	if (hKeyFound != NULL)
+	    break;
+#  endif
+    }
+    if (hKeyFound == NULL)
 	return NULL;
-    cp = installpath + len;
-    // Just in case registry value contains null terminators.
-    while (cp > installpath && *(cp-1) == '\0')
-	--cp;
+
+    swprintf(subkey, ARRAY_LENGTH(subkey), L"%ls\\%ls\\InstallPath",
+							pythoncore, keyfound);
+    ret = RegGetValueW(hKeyFound, subkey, NULL, RRF_RT_REG_SZ,
+							    NULL, NULL, &len);
+    if (ret != ERROR_MORE_DATA && ret != ERROR_SUCCESS)
+	return NULL;
+    size_t len2 = len / sizeof(WCHAR) + 1 + strlen(libname);
+    WCHAR *path = alloc(len2 * sizeof(WCHAR));
+    if (path == NULL)
+	return NULL;
+    ret = RegGetValueW(hKeyFound, subkey, NULL, RRF_RT_REG_SZ,
+							    NULL, path, &len);
+    if (ret != ERROR_SUCCESS)
+    {
+	vim_free(path);
+	return NULL;
+    }
     // Remove trailing path separators.
-    while (cp > installpath && (*(cp-1) == '\\' || *(cp-1) == '/'))
-	--cp;
-    // Ignore if InstallPath is effectively empty.
-    if (cp <= installpath)
-	return NULL;
-    sysliblen = (cp - installpath) + 1 + STRLEN(libname) + 1;
-    syslibname = alloc(sysliblen);
-    vim_snprintf(syslibname, sysliblen, "%.*s\\%s",
-				(int)(cp - installpath), installpath, libname);
-    return syslibname;
+    size_t len3 = wcslen(path);
+    if ((len3 > 0) && (path[len3 - 1] == L'/' || path[len3 - 1] == L'\\'))
+	--len3;
+    swprintf(path + len3, len2 - len3, L"\\%hs", libname);
+    return path;
 }
 # endif
 
@@ -923,11 +982,13 @@ py3_runtime_link_init(char *libname, int
     if (!hinstPy3)
     {
 	// Attempt to use the path from InstallPath as stored in the registry.
-	char *syslibname = py3_get_system_libname(libname);
+	WCHAR *syslibname = py3_get_system_libname(libname);
 
 	if (syslibname != NULL)
 	{
-	    hinstPy3 = load_dll(syslibname);
+	    hinstPy3 = LoadLibraryExW(syslibname, NULL,
+		    LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR |
+		    LOAD_LIBRARY_SEARCH_SYSTEM32);
 	    vim_free(syslibname);
 	}
     }
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    2026,
+/**/
     2025,
 /**/
     2024,