Skip to content

Commit ae3cfa4

Browse files
k-takatachrisbra
authored andcommitted
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 <[email protected]> Co-authored-by: Ken Takata <[email protected]>
1 parent 989426b commit ae3cfa4

File tree

3 files changed

+101
-34
lines changed

3 files changed

+101
-34
lines changed

runtime/doc/if_pyth.txt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -754,8 +754,10 @@ you can use Vim without this file.
754754
MS-Windows ~
755755

756756
To use the Python interface the Python DLL must be in your search path. In a
757-
console window type "path" to see what directories are used. The 'pythondll'
758-
or 'pythonthreedll' option can be also used to specify the Python DLL.
757+
console window type "path" to see what directories are used. If the DLL is
758+
not found in your search path, Vim will check the registry to find the path
759+
where Python is installed. The 'pythondll' or 'pythonthreedll' option can be
760+
also used to specify the Python DLL.
759761

760762
The name of the DLL should match the Python version Vim was compiled with.
761763
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
782784
library with version of at least |v:python3_version| will work. See
783785
|has-python| for how to check if Stable ABI is supported, or see if version
784786
output includes |+python3/dyn-stable|.
787+
On MS-Windows, 'pythonthreedll' will be set to "python3.dll". When searching
788+
the DLL from the registry, Vim will search the latest version of Python.
785789

786790
==============================================================================
787791
10. Python 3 *python3*

src/if_python3.c

Lines changed: 93 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -835,17 +835,16 @@ Py_ssize_t py3_PyList_GET_SIZE(PyObject *op)
835835
* Look up the library "libname" using the InstallPath registry key.
836836
* Return NULL when failed. Return an allocated string when successful.
837837
*/
838-
static char *
838+
static WCHAR *
839839
py3_get_system_libname(const char *libname)
840840
{
841+
const WCHAR *pythoncore = L"Software\\Python\\PythonCore";
841842
const char *cp = libname;
842-
char subkey[128];
843+
WCHAR subkey[128];
843844
HKEY hKey;
844-
char installpath[MAXPATHL];
845-
LONG len = sizeof(installpath);
846-
LSTATUS rc;
847-
size_t sysliblen;
848-
char *syslibname;
845+
int i;
846+
DWORD j, len;
847+
LSTATUS ret;
849848

850849
while (*cp != '\0')
851850
{
@@ -857,35 +856,95 @@ py3_get_system_libname(const char *libname)
857856
}
858857
++cp;
859858
}
860-
vim_snprintf(subkey, sizeof(subkey),
859+
860+
WCHAR keyfound[32];
861+
HKEY hKeyTop[] = {HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE};
862+
HKEY hKeyFound = NULL;
863+
# ifdef USE_LIMITED_API
864+
long maxminor = -1;
865+
# endif
866+
for (i = 0; i < ARRAY_LENGTH(hKeyTop); i++)
867+
{
868+
long major, minor;
869+
870+
ret = RegOpenKeyExW(hKeyTop[i], pythoncore, 0, KEY_READ, &hKey);
871+
if (ret != ERROR_SUCCESS)
872+
continue;
873+
for (j = 0;; j++)
874+
{
875+
WCHAR keyname[32];
876+
WCHAR *wp;
877+
878+
len = ARRAY_LENGTH(keyname);
879+
ret = RegEnumKeyExW(hKey, j, keyname, &len,
880+
NULL, NULL, NULL, NULL);
881+
if (ret == ERROR_NO_MORE_ITEMS)
882+
break;
883+
884+
major = wcstol(keyname, &wp, 10);
885+
if (*wp == L'.')
886+
minor = wcstol(wp + 1, &wp, 10);
861887
# ifdef _WIN64
862-
"Software\\Python\\PythonCore\\%d.%d\\InstallPath",
888+
if (*wp != L'\0')
889+
continue;
863890
# else
864-
"Software\\Python\\PythonCore\\%d.%d-32\\InstallPath",
891+
if (wcscmp(wp, L"-32") != 0)
892+
continue;
865893
# endif
866-
PY_MAJOR_VERSION, PY_MINOR_VERSION);
867-
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, subkey, 0, KEY_QUERY_VALUE, &hKey)
868-
!= ERROR_SUCCESS)
894+
895+
if (major != PY_MAJOR_VERSION)
896+
continue;
897+
# ifdef USE_LIMITED_API
898+
// Search the latest version.
899+
if ((minor > maxminor)
900+
&& (minor >= ((Py_LIMITED_API >> 16) & 0xff)))
901+
{
902+
maxminor = minor;
903+
wcscpy(keyfound, keyname);
904+
hKeyFound = hKeyTop[i];
905+
}
906+
# else
907+
// Check if it matches with the compiled version.
908+
if (minor == PY_MINOR_VERSION)
909+
{
910+
wcscpy(keyfound, keyname);
911+
hKeyFound = hKeyTop[i];
912+
break;
913+
}
914+
# endif
915+
}
916+
RegCloseKey(hKey);
917+
# ifdef USE_LIMITED_API
918+
if (hKeyFound != NULL)
919+
break;
920+
# endif
921+
}
922+
if (hKeyFound == NULL)
869923
return NULL;
870-
rc = RegQueryValueA(hKey, NULL, installpath, &len);
871-
RegCloseKey(hKey);
872-
if (ERROR_SUCCESS != rc)
924+
925+
swprintf(subkey, ARRAY_LENGTH(subkey), L"%ls\\%ls\\InstallPath",
926+
pythoncore, keyfound);
927+
ret = RegGetValueW(hKeyFound, subkey, NULL, RRF_RT_REG_SZ,
928+
NULL, NULL, &len);
929+
if (ret != ERROR_MORE_DATA && ret != ERROR_SUCCESS)
873930
return NULL;
874-
cp = installpath + len;
875-
// Just in case registry value contains null terminators.
876-
while (cp > installpath && *(cp-1) == '\0')
877-
--cp;
878-
// Remove trailing path separators.
879-
while (cp > installpath && (*(cp-1) == '\\' || *(cp-1) == '/'))
880-
--cp;
881-
// Ignore if InstallPath is effectively empty.
882-
if (cp <= installpath)
931+
size_t len2 = len / sizeof(WCHAR) + 1 + strlen(libname);
932+
WCHAR *path = alloc(len2 * sizeof(WCHAR));
933+
if (path == NULL)
883934
return NULL;
884-
sysliblen = (cp - installpath) + 1 + STRLEN(libname) + 1;
885-
syslibname = alloc(sysliblen);
886-
vim_snprintf(syslibname, sysliblen, "%.*s\\%s",
887-
(int)(cp - installpath), installpath, libname);
888-
return syslibname;
935+
ret = RegGetValueW(hKeyFound, subkey, NULL, RRF_RT_REG_SZ,
936+
NULL, path, &len);
937+
if (ret != ERROR_SUCCESS)
938+
{
939+
vim_free(path);
940+
return NULL;
941+
}
942+
// Remove trailing path separators.
943+
size_t len3 = wcslen(path);
944+
if ((len3 > 0) && (path[len3 - 1] == L'/' || path[len3 - 1] == L'\\'))
945+
--len3;
946+
swprintf(path + len3, len2 - len3, L"\\%hs", libname);
947+
return path;
889948
}
890949
# endif
891950

@@ -923,11 +982,13 @@ py3_runtime_link_init(char *libname, int verbose)
923982
if (!hinstPy3)
924983
{
925984
// Attempt to use the path from InstallPath as stored in the registry.
926-
char *syslibname = py3_get_system_libname(libname);
985+
WCHAR *syslibname = py3_get_system_libname(libname);
927986

928987
if (syslibname != NULL)
929988
{
930-
hinstPy3 = load_dll(syslibname);
989+
hinstPy3 = LoadLibraryExW(syslibname, NULL,
990+
LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR |
991+
LOAD_LIBRARY_SEARCH_SYSTEM32);
931992
vim_free(syslibname);
932993
}
933994
}

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,8 @@ static char *(features[]) =
704704

705705
static int included_patches[] =
706706
{ /* Add new patch number below this line */
707+
/**/
708+
2026,
707709
/**/
708710
2025,
709711
/**/

0 commit comments

Comments
 (0)