r69038 - in python/trunk: PC/dl_nt.c Python/dynload_win.c

Author: mark.hammond Date: Wed Jan 28 00:12:23 2009 New Revision: 69038 Log: On Windows, use the Python 'Activation Context' when loading extensions to avoid problems loading the CRT from a private assembly. Via bug 4566. Modified: python/trunk/PC/dl_nt.c python/trunk/Python/dynload_win.c Modified: python/trunk/PC/dl_nt.c ============================================================================== --- python/trunk/PC/dl_nt.c (original) +++ python/trunk/PC/dl_nt.c Wed Jan 28 00:12:23 2009 @@ -18,6 +18,63 @@ HMODULE PyWin_DLLhModule = NULL; const char *PyWin_DLLVersionString = dllVersionBuffer; +// Windows "Activation Context" work: +// Our .pyd extension modules are generally built without a manifest (ie, +// those included with Python and those built with a default distutils. +// This requires we perform some "activation context" magic when loading our +// extensions. In summary: +// * As our DLL loads we save the context being used. +// * Before loading our extensions we re-activate our saved context. +// * After extension load is complete we restore the old context. +// As an added complication, this magic only works on XP or later - we simply +// use the existence (or not) of the relevant function pointers from kernel32. +// See bug 4566 (http://python.org/sf/4566) for more details. + +typedef BOOL (WINAPI * PFN_GETCURRENTACTCTX)(HANDLE *); +typedef BOOL (WINAPI * PFN_ACTIVATEACTCTX)(HANDLE, ULONG_PTR *); +typedef BOOL (WINAPI * PFN_DEACTIVATEACTCTX)(DWORD, ULONG_PTR); +typedef BOOL (WINAPI * PFN_ADDREFACTCTX)(HANDLE); +typedef BOOL (WINAPI * PFN_RELEASEACTCTX)(HANDLE); + +// locals and function pointers for this activation context magic. +static HANDLE PyWin_DLLhActivationContext = NULL; // one day it might be public +static PFN_GETCURRENTACTCTX pfnGetCurrentActCtx = NULL; +static PFN_ACTIVATEACTCTX pfnActivateActCtx = NULL; +static PFN_DEACTIVATEACTCTX pfnDeactivateActCtx = NULL; +static PFN_ADDREFACTCTX pfnAddRefActCtx = NULL; +static PFN_RELEASEACTCTX pfnReleaseActCtx = NULL; + +void _LoadActCtxPointers() +{ + HINSTANCE hKernel32 = GetModuleHandleW(L"kernel32.dll"); + if (hKernel32) + pfnGetCurrentActCtx = (PFN_GETCURRENTACTCTX) GetProcAddress(hKernel32, "GetCurrentActCtx"); + // If we can't load GetCurrentActCtx (ie, pre XP) , don't bother with the rest. + if (pfnGetCurrentActCtx) { + pfnActivateActCtx = (PFN_ACTIVATEACTCTX) GetProcAddress(hKernel32, "ActivateActCtx"); + pfnDeactivateActCtx = (PFN_DEACTIVATEACTCTX) GetProcAddress(hKernel32, "DeactivateActCtx"); + pfnAddRefActCtx = (PFN_ADDREFACTCTX) GetProcAddress(hKernel32, "AddRefActCtx"); + pfnReleaseActCtx = (PFN_RELEASEACTCTX) GetProcAddress(hKernel32, "ReleaseActCtx"); + } +} + +ULONG_PTR _Py_ActivateActCtx() +{ + ULONG_PTR ret = 0; + if (PyWin_DLLhActivationContext && pfnActivateActCtx) + if (!(*pfnActivateActCtx)(PyWin_DLLhActivationContext, &ret)) { + OutputDebugString("Python failed to activate the activation context before loading a DLL\n"); + ret = 0; // no promise the failing function didn't change it! + } + return ret; +} + +void _Py_DeactivateActCtx(ULONG_PTR cookie) +{ + if (cookie && pfnDeactivateActCtx) + if (!(*pfnDeactivateActCtx)(0, cookie)) + OutputDebugString("Python failed to de-activate the activation context\n"); +} BOOL WINAPI DllMain (HANDLE hInst, ULONG ul_reason_for_call, @@ -29,9 +86,18 @@ PyWin_DLLhModule = hInst; // 1000 is a magic number I picked out of the air. Could do with a #define, I spose... LoadString(hInst, 1000, dllVersionBuffer, sizeof(dllVersionBuffer)); - //initall(); + + // and capture our activation context for use when loading extensions. + _LoadActCtxPointers(); + if (pfnGetCurrentActCtx && pfnAddRefActCtx) + if ((*pfnGetCurrentActCtx)(&PyWin_DLLhActivationContext)) + if (!(*pfnAddRefActCtx)(PyWin_DLLhActivationContext)) + OutputDebugString("Python failed to load the default activation context\n"); break; + case DLL_PROCESS_DETACH: + if (pfnReleaseActCtx) + (*pfnReleaseActCtx)(PyWin_DLLhActivationContext); break; } return TRUE; Modified: python/trunk/Python/dynload_win.c ============================================================================== --- python/trunk/Python/dynload_win.c (original) +++ python/trunk/Python/dynload_win.c Wed Jan 28 00:12:23 2009 @@ -11,6 +11,10 @@ #include "importdl.h" #include <windows.h> +// "activation context" magic - see dl_nt.c... +extern ULONG_PTR _Py_ActivateActCtx(); +void _Py_DeactivateActCtx(ULONG_PTR cookie); + const struct filedescr _PyImport_DynLoadFiletab[] = { #ifdef _DEBUG {"_d.pyd", "rb", C_EXTENSION}, @@ -172,6 +176,7 @@ char pathbuf[260]; LPTSTR dummy; unsigned int old_mode; + ULONG_PTR cookie = 0; /* We use LoadLibraryEx so Windows looks for dependent DLLs in directory of pathname first. However, Windows95 can sometimes not work correctly unless the absolute @@ -184,10 +189,13 @@ if (GetFullPathName(pathname, sizeof(pathbuf), pathbuf, - &dummy)) + &dummy)) { + ULONG_PTR cookie = _Py_ActivateActCtx(); /* XXX This call doesn't exist in Windows CE */ hDLL = LoadLibraryEx(pathname, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + _Py_DeactivateActCtx(cookie); + } /* restore old error mode settings */ SetErrorMode(old_mode);

Mark> +// Windows "Activation Context" work: Mark> +// Our .pyd extension modules are generally built without a manifest (ie, Mark> +// those included with Python and those built with a default distutils. Mark> +// This requires we perform some "activation context" magic when loading our Mark> +// extensions. In summary: Mark> +// * As our DLL loads we save the context being used. Mark> +// * Before loading our extensions we re-activate our saved context. Mark> +// * After extension load is complete we restore the old context. Mark> +// As an added complication, this magic only works on XP or later - we simply Mark> +// use the existence (or not) of the relevant function pointers from kernel32. Mark> +// See bug 4566 (http://python.org/sf/4566) for more details. Shouldn't these be c-style comments? (Again, sorry if I'm way behind...) Skip
participants (2)
-
mark.hammond
-
skip@pobox.com