@@ -133,6 +133,7 @@ char meta_name[80]; /* package name without version like
133133char install_script [MAX_PATH ];
134134char * pre_install_script ; /* run before we install a single file */
135135
136+ char user_access_control [10 ]; // one of 'auto', 'force', otherwise none.
136137
137138int py_major , py_minor ; /* Python version selected for installation */
138139
@@ -344,8 +345,15 @@ struct PyMethodDef {
344345};
345346typedef struct PyMethodDef PyMethodDef ;
346347
348+ // XXX - all of these are potentially fragile! We load and unload
349+ // the Python DLL multiple times - so storing functions pointers
350+ // is dangerous (although things *look* OK at present)
351+ // Better might be to roll prepare_script_environment() into
352+ // LoadPythonDll(), and create a new UnloadPythonDLL() which also
353+ // clears the global pointers.
347354void * (* g_Py_BuildValue )(char * , ...);
348355int (* g_PyArg_ParseTuple )(PyObject * , char * , ...);
356+ PyObject * (* g_PyLong_FromVoidPtr )(void * );
349357
350358PyObject * g_PyExc_ValueError ;
351359PyObject * g_PyExc_OSError ;
@@ -597,7 +605,7 @@ static PyObject *PyMessageBox(PyObject *self, PyObject *args)
597605
598606static PyObject * GetRootHKey (PyObject * self )
599607{
600- return g_Py_BuildValue ( "l" , hkey_root );
608+ return g_PyLong_FromVoidPtr ( hkey_root );
601609}
602610
603611#define METH_VARARGS 0x0001
@@ -631,7 +639,9 @@ static HINSTANCE LoadPythonDll(char *fname)
631639 "SOFTWARE\\Python\\PythonCore\\%d.%d\\InstallPath" ,
632640 py_major , py_minor );
633641 if (ERROR_SUCCESS != RegQueryValue (HKEY_CURRENT_USER , subkey_name ,
634- fullpath , & size ))
642+ fullpath , & size ) &&
643+ ERROR_SUCCESS != RegQueryValue (HKEY_LOCAL_MACHINE , subkey_name ,
644+ fullpath , & size ))
635645 return NULL ;
636646 strcat (fullpath , "\\" );
637647 strcat (fullpath , fname );
@@ -648,6 +658,7 @@ static int prepare_script_environment(HINSTANCE hPython)
648658 DECLPROC (hPython , PyObject * , Py_BuildValue , (char * , ...) );
649659 DECLPROC (hPython , int , PyArg_ParseTuple , (PyObject * , char * , ...) );
650660 DECLPROC (hPython , PyObject * , PyErr_Format , (PyObject * , char * ) );
661+ DECLPROC (hPython , PyObject * , PyLong_FromVoidPtr , (void * ) );
651662 if (!PyImport_ImportModule || !PyObject_GetAttrString ||
652663 !PyObject_SetAttrString || !PyCFunction_New )
653664 return 1 ;
@@ -667,6 +678,7 @@ static int prepare_script_environment(HINSTANCE hPython)
667678 g_Py_BuildValue = Py_BuildValue ;
668679 g_PyArg_ParseTuple = PyArg_ParseTuple ;
669680 g_PyErr_Format = PyErr_Format ;
681+ g_PyLong_FromVoidPtr = PyLong_FromVoidPtr ;
670682
671683 return 0 ;
672684}
@@ -777,7 +789,9 @@ static int run_simple_script(char *script)
777789
778790 hPython = LoadPythonDll (pythondll );
779791 if (!hPython ) {
780- set_failure_reason ("Can't load Python for pre-install script" );
792+ char reason [128 ];
793+ wsprintf (reason , "Can't load Python for pre-install script (%d)" , GetLastError ());
794+ set_failure_reason (reason );
781795 return -1 ;
782796 }
783797 rc = do_run_simple_script (hPython , script );
@@ -2073,6 +2087,71 @@ void RunWizard(HWND hwnd)
20732087 PropertySheet (& psh );
20742088}
20752089
2090+ // subtly different from HasLocalMachinePrivs(), in that after executing
2091+ // an 'elevated' process, we expect this to return TRUE - but there is no
2092+ // such implication for HasLocalMachinePrivs
2093+ BOOL MyIsUserAnAdmin ()
2094+ {
2095+ typedef BOOL (WINAPI * PFNIsUserAnAdmin )( );
2096+ static PFNIsUserAnAdmin pfnIsUserAnAdmin = NULL ;
2097+ HMODULE shell32 ;
2098+ // This function isn't guaranteed to be available (and it can't hurt
2099+ // to leave the library loaded)
2100+ if (0 == (shell32 = LoadLibrary ("shell32.dll" )))
2101+ return FALSE;
2102+ if (0 == (pfnIsUserAnAdmin = (PFNIsUserAnAdmin )GetProcAddress (shell32 , "IsUserAnAdmin" )))
2103+ return FALSE;
2104+ return (* pfnIsUserAnAdmin )();
2105+ }
2106+
2107+ // Some magic for Vista's UAC. If there is a target_version, and
2108+ // if that target version is installed in the registry under
2109+ // HKLM, and we are not current administrator, then
2110+ // re-execute ourselves requesting elevation.
2111+ // Split into 2 functions - "should we elevate" and "spawn elevated"
2112+
2113+ // Returns TRUE if we should spawn an elevated child
2114+ BOOL NeedAutoUAC ()
2115+ {
2116+ HKEY hk ;
2117+ char key_name [80 ];
2118+ OSVERSIONINFO winverinfo ;
2119+ winverinfo .dwOSVersionInfoSize = sizeof (winverinfo );
2120+ // If less than XP, then we can't do it (and its not necessary).
2121+ if (!GetVersionEx (& winverinfo ) || winverinfo .dwMajorVersion < 5 )
2122+ return FALSE;
2123+ // no Python version info == we can't know yet.
2124+ if (target_version [0 ] == '\0' )
2125+ return FALSE;
2126+ // see how python is current installed
2127+ wsprintf (key_name ,
2128+ "Software\\Python\\PythonCore\\%s\\InstallPath" ,
2129+ target_version );
2130+ if (ERROR_SUCCESS != RegOpenKeyEx (HKEY_LOCAL_MACHINE ,
2131+ key_name , 0 , KEY_READ , & hk ))
2132+ return FALSE;
2133+ RegCloseKey (hk );
2134+ // Python is installed in HKLM - we must elevate.
2135+ return TRUE;
2136+ }
2137+
2138+ // Spawn ourself as an elevated application. On failure, a message is
2139+ // displayed to the user - but this app will always terminate, even
2140+ // on error.
2141+ void SpawnUAC ()
2142+ {
2143+ // interesting failure scenario that has been seen: initial executable
2144+ // runs from a network drive - but once elevated, that network share
2145+ // isn't seen, and ShellExecute fails with SE_ERR_ACCESSDENIED.
2146+ int ret = (int )ShellExecute (0 , "runas" , modulename , "" , NULL ,
2147+ SW_SHOWNORMAL );
2148+ if (ret <= 32 ) {
2149+ char msg [128 ];
2150+ wsprintf (msg , "Failed to start elevated process (ShellExecute returned %d)" , ret );
2151+ MessageBox (0 , msg , "Setup" , MB_OK | MB_ICONERROR );
2152+ }
2153+ }
2154+
20762155int DoInstall (void )
20772156{
20782157 char ini_buffer [4096 ];
@@ -2106,6 +2185,31 @@ int DoInstall(void)
21062185 install_script , sizeof (install_script ),
21072186 ini_file );
21082187
2188+ GetPrivateProfileString ("Setup" , "user_access_control" , "" ,
2189+ user_access_control , sizeof (user_access_control ), ini_file );
2190+
2191+ // See if we need to do the Vista UAC magic.
2192+ if (strcmp (user_access_control , "force" )== 0 ) {
2193+ if (!MyIsUserAnAdmin ()) {
2194+ SpawnUAC ();
2195+ return 0 ;
2196+ }
2197+ // already admin - keep going
2198+ } else if (strcmp (user_access_control , "auto" )== 0 ) {
2199+ // Check if it looks like we need UAC control, based
2200+ // on how Python itself was installed.
2201+ if (!MyIsUserAnAdmin () && NeedAutoUAC ()) {
2202+ SpawnUAC ();
2203+ return 0 ;
2204+ }
2205+ } else {
2206+ // display a warning about unknown values - only the developer
2207+ // of the extension will see it (until they fix it!)
2208+ if (user_access_control [0 ] && strcmp (user_access_control , "none" ) != 0 ) {
2209+ MessageBox (GetFocus (), "Bad user_access_control value" , "oops" , MB_OK );
2210+ // nothing to do.
2211+ }
2212+ }
21092213
21102214 hwndMain = CreateBackground (title );
21112215
0 commit comments