0
0
mirror of https://github.com/vim/vim.git synced 2025-09-25 03:54:15 -04:00

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>
This commit is contained in:
Ken Takata
2023-10-14 11:49:09 +02:00
committed by Christian Brabandt
parent 989426be6e
commit ae3cfa47d3
3 changed files with 103 additions and 36 deletions

View 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),
# 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)
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;
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;
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 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);
}
}