]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.0.2026: win32: python3 dll loading can be improved v9.0.2026
authorKen Takata <kentkt@csc.jp>
Sat, 14 Oct 2023 09:49:09 +0000 (11:49 +0200)
committerChristian Brabandt <cb@256bit.org>
Sat, 14 Oct 2023 09:49:09 +0000 (11:49 +0200)
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>
runtime/doc/if_pyth.txt
src/if_python3.c
src/version.c

index 3d3b92a662ce10de728205b3072f327f348a8821..c0b968e07c9a2ba58e82255f41ba19a852408d72 100644 (file)
@@ -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 restriction is relaxed, and any Python 3
 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*
index c3900892ea10191ff00d3d6683af41d1ebdb4a5a..a1773717202c663d9e780fca64eebb412b9d0580 100644 (file)
@@ -835,17 +835,16 @@ Py_ssize_t py3_PyList_GET_SIZE(PyObject *op)
  * 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 *libname)
        }
        ++cp;
     }
-    vim_snprintf(subkey, sizeof(subkey),
+
+    WCHAR   keyfound[32];
+    HKEY    hKeyTop[] = {HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE};
+    HKEY    hKeyFound = NULL;
+#  ifdef USE_LIMITED_API
+    long    maxminor = -1;
+#  endif
+    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
-                "Software\\Python\\PythonCore\\%d.%d\\InstallPath",
+           if (*wp != L'\0')
+               continue;
 #  else
-                "Software\\Python\\PythonCore\\%d.%d-32\\InstallPath",
+           if (wcscmp(wp, L"-32") != 0)
+               continue;
 #  endif
-                PY_MAJOR_VERSION, PY_MINOR_VERSION);
-    if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, subkey, 0, KEY_QUERY_VALUE, &hKey)
-                                                             != ERROR_SUCCESS)
+
+           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;
-    rc = RegQueryValueA(hKey, NULL, installpath, &len);
-    RegCloseKey(hKey);
-    if (ERROR_SUCCESS != rc)
+
+    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;
-    cp = installpath + len;
-    // Just in case registry value contains null terminators.
-    while (cp > installpath && *(cp-1) == '\0')
-       --cp;
-    // Remove trailing path separators.
-    while (cp > installpath && (*(cp-1) == '\\' || *(cp-1) == '/'))
-       --cp;
-    // Ignore if InstallPath is effectively empty.
-    if (cp <= installpath)
+    size_t len2 = len / sizeof(WCHAR) + 1 + strlen(libname);
+    WCHAR *path = alloc(len2 * sizeof(WCHAR));
+    if (path == NULL)
        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;
+    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.
+    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 verbose)
     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);
        }
     }
index 73a573df58680fe1112839c4d83e7dad2d3d5543..111254eade1f2f41eedfc69c766fc88de87da14d 100644 (file)
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2026,
 /**/
     2025,
 /**/