Copying a file in python : Permission denied [duplicate] - python

I want my Python script to copy files on Vista. When I run it from a normal cmd.exe window, no errors are generated, yet the files are NOT copied. If I run cmd.exe "as administator" and then run my script, it works fine.
This makes sense since User Account Control (UAC) normally prevents many file system actions.
Is there a way I can, from within a Python script, invoke a UAC elevation request (those dialogs that say something like "such and such app needs admin access, is this OK?")
If that's not possible, is there a way my script can at least detect that it is not elevated so it can fail gracefully?

As of 2017, an easy method to achieve this is the following:
import ctypes, sys
def is_admin():
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
if is_admin():
# Code of your program here
else:
# Re-run the program with admin rights
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1)
If you are using Python 2.x, then you should replace the last line for:
ctypes.windll.shell32.ShellExecuteW(None, u"runas", unicode(sys.executable), unicode(" ".join(sys.argv)), None, 1)
Also note that if you converted you python script into an executable file (using tools like py2exe, cx_freeze, pyinstaller) then you should use sys.argv[1:] instead of sys.argv in the fourth parameter.
Some of the advantages here are:
No external libraries required. It only uses ctypes and sys from standard library.
Works on both Python 2 and Python 3.
There is no need to modify the file resources nor creating a manifest file.
If you don't add code below if/else statement, the code won't ever be executed twice.
You can get the return value of the API call in the last line and take an action if it fails (code <= 32). Check possible return values here.
You can change the display method of the spawned process modifying the sixth parameter.
Documentation for the underlying ShellExecute call is here.

It took me a little while to get dguaraglia's answer working, so in the interest of saving others time, here's what I did to implement this idea:
import os
import sys
import win32com.shell.shell as shell
ASADMIN = 'asadmin'
if sys.argv[-1] != ASADMIN:
script = os.path.abspath(sys.argv[0])
params = ' '.join([script] + sys.argv[1:] + [ASADMIN])
shell.ShellExecuteEx(lpVerb='runas', lpFile=sys.executable, lpParameters=params)
sys.exit(0)

It seems there's no way to elevate the application privileges for a while for you to perform a particular task. Windows needs to know at the start of the program whether the application requires certain privileges, and will ask the user to confirm when the application performs any tasks that need those privileges. There are two ways to do this:
Write a manifest file that tells Windows the application might require some privileges
Run the application with elevated privileges from inside another program
This two articles explain in much more detail how this works.
What I'd do, if you don't want to write a nasty ctypes wrapper for the CreateElevatedProcess API, is use the ShellExecuteEx trick explained in the Code Project article (Pywin32 comes with a wrapper for ShellExecute). How? Something like this:
When your program starts, it checks if it has Administrator privileges, if it doesn't it runs itself using the ShellExecute trick and exits immediately, if it does, it performs the task at hand.
As you describe your program as a "script", I suppose that's enough for your needs.
Cheers.

Just adding this answer in case others are directed here by Google Search as I was.
I used the elevate module in my Python script and the script executed with Administrator Privileges in Windows 10.
https://pypi.org/project/elevate/

The following example builds on MARTIN DE LA FUENTE SAAVEDRA's excellent work and accepted answer. In particular, two enumerations are introduced. The first allows for easy specification of how an elevated program is to be opened, and the second helps when errors need to be easily identified. Please note that if you want all command line arguments passed to the new process, sys.argv[0] should probably be replaced with a function call: subprocess.list2cmdline(sys.argv).
#! /usr/bin/env python3
import ctypes
import enum
import subprocess
import sys
# Reference:
# msdn.microsoft.com/en-us/library/windows/desktop/bb762153(v=vs.85).aspx
# noinspection SpellCheckingInspection
class SW(enum.IntEnum):
HIDE = 0
MAXIMIZE = 3
MINIMIZE = 6
RESTORE = 9
SHOW = 5
SHOWDEFAULT = 10
SHOWMAXIMIZED = 3
SHOWMINIMIZED = 2
SHOWMINNOACTIVE = 7
SHOWNA = 8
SHOWNOACTIVATE = 4
SHOWNORMAL = 1
class ERROR(enum.IntEnum):
ZERO = 0
FILE_NOT_FOUND = 2
PATH_NOT_FOUND = 3
BAD_FORMAT = 11
ACCESS_DENIED = 5
ASSOC_INCOMPLETE = 27
DDE_BUSY = 30
DDE_FAIL = 29
DDE_TIMEOUT = 28
DLL_NOT_FOUND = 32
NO_ASSOC = 31
OOM = 8
SHARE = 26
def bootstrap():
if ctypes.windll.shell32.IsUserAnAdmin():
main()
else:
# noinspection SpellCheckingInspection
hinstance = ctypes.windll.shell32.ShellExecuteW(
None,
'runas',
sys.executable,
subprocess.list2cmdline(sys.argv),
None,
SW.SHOWNORMAL
)
if hinstance <= 32:
raise RuntimeError(ERROR(hinstance))
def main():
# Your Code Here
print(input('Echo: '))
if __name__ == '__main__':
bootstrap()

Recognizing this question was asked years ago, I think a more elegant solution is offered on github by frmdstryr using his module pywinutils:
Excerpt:
import pythoncom
from win32com.shell import shell,shellcon
def copy(src,dst,flags=shellcon.FOF_NOCONFIRMATION):
""" Copy files using the built in Windows File copy dialog
Requires absolute paths. Does NOT create root destination folder if it doesn't exist.
Overwrites and is recursive by default
#see http://msdn.microsoft.com/en-us/library/bb775799(v=vs.85).aspx for flags available
"""
# #see IFileOperation
pfo = pythoncom.CoCreateInstance(shell.CLSID_FileOperation,None,pythoncom.CLSCTX_ALL,shell.IID_IFileOperation)
# Respond with Yes to All for any dialog
# #see http://msdn.microsoft.com/en-us/library/bb775799(v=vs.85).aspx
pfo.SetOperationFlags(flags)
# Set the destionation folder
dst = shell.SHCreateItemFromParsingName(dst,None,shell.IID_IShellItem)
if type(src) not in (tuple,list):
src = (src,)
for f in src:
item = shell.SHCreateItemFromParsingName(f,None,shell.IID_IShellItem)
pfo.CopyItem(item,dst) # Schedule an operation to be performed
# #see http://msdn.microsoft.com/en-us/library/bb775780(v=vs.85).aspx
success = pfo.PerformOperations()
# #see sdn.microsoft.com/en-us/library/bb775769(v=vs.85).aspx
aborted = pfo.GetAnyOperationsAborted()
return success is None and not aborted
This utilizes the COM interface and automatically indicates that admin privileges are needed with the familiar dialog prompt that you would see if you were copying into a directory where admin privileges are required and also provides the typical file progress dialog during the copy operation.

This may not completely answer your question but you could also try using the Elevate Command Powertoy in order to run the script with elevated UAC privileges.
http://technet.microsoft.com/en-us/magazine/2008.06.elevation.aspx
I think if you use it it would look like 'elevate python yourscript.py'

You can make a shortcut somewhere and as the target use:
python yourscript.py
then under properties and advanced select run as administrator.
When the user executes the shortcut it will ask them to elevate the application.

A variation on Jorenko's work above allows the elevated process to use the same console (but see my comment below):
def spawn_as_administrator():
""" Spawn ourself with administrator rights and wait for new process to exit
Make the new process use the same console as the old one.
Raise Exception() if we could not get a handle for the new re-run the process
Raise pywintypes.error() if we could not re-spawn
Return the exit code of the new process,
or return None if already running the second admin process. """
#pylint: disable=no-name-in-module,import-error
import win32event, win32api, win32process
import win32com.shell.shell as shell
if '--admin' in sys.argv:
return None
script = os.path.abspath(sys.argv[0])
params = ' '.join([script] + sys.argv[1:] + ['--admin'])
SEE_MASK_NO_CONSOLE = 0x00008000
SEE_MASK_NOCLOSE_PROCESS = 0x00000040
process = shell.ShellExecuteEx(lpVerb='runas', lpFile=sys.executable, lpParameters=params, fMask=SEE_MASK_NO_CONSOLE|SEE_MASK_NOCLOSE_PROCESS)
hProcess = process['hProcess']
if not hProcess:
raise Exception("Could not identify administrator process to install drivers")
# It is necessary to wait for the elevated process or else
# stdin lines are shared between 2 processes: they get one line each
INFINITE = -1
win32event.WaitForSingleObject(hProcess, INFINITE)
exitcode = win32process.GetExitCodeProcess(hProcess)
win32api.CloseHandle(hProcess)
return exitcode

This is mostly an upgrade to Jorenko's answer, that allows to use parameters with spaces in Windows, but should also work fairly well on Linux :)
Also, will work with cx_freeze or py2exe since we don't use __file__ but sys.argv[0] as executable
[EDIT]
Disclaimer: The code in this post is outdated.
I have published the elevation code as a python package.
Install with pip install command_runner
Usage:
from command_runner.elevate import elevate
def main():
"""My main function that should be elevated"""
print("Who's the administrator, now ?")
if __name__ == '__main__':
elevate(main)
[/EDIT]
import sys,ctypes,platform
def is_admin():
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
raise False
if __name__ == '__main__':
if platform.system() == "Windows":
if is_admin():
main(sys.argv[1:])
else:
# Re-run the program with admin rights, don't use __file__ since py2exe won't know about it
# Use sys.argv[0] as script path and sys.argv[1:] as arguments, join them as lpstr, quoting each parameter or spaces will divide parameters
lpParameters = ""
# Litteraly quote all parameters which get unquoted when passed to python
for i, item in enumerate(sys.argv[0:]):
lpParameters += '"' + item + '" '
try:
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, lpParameters , None, 1)
except:
sys.exit(1)
else:
main(sys.argv[1:])

For one-liners, put the code to where you need UAC.
Request UAC, if failed, keep running:
import ctypes, sys
ctypes.windll.shell32.IsUserAnAdmin() or ctypes.windll.shell32.ShellExecuteW(
None, "runas", sys.executable, " ".join(sys.argv), None, 1) > 32 and exit()
Request UAC, if failed, exit:
import ctypes, sys
ctypes.windll.shell32.IsUserAnAdmin() or (ctypes.windll.shell32.ShellExecuteW(
None, "runas", sys.executable, " ".join(sys.argv), None, 1) > 32, exit())
Function style:
# Created by BaiJiFeiLong#gmail.com at 2022/6/24
import ctypes
import sys
def request_uac_or_skip():
ctypes.windll.shell32.IsUserAnAdmin() or ctypes.windll.shell32.ShellExecuteW(
None, "runas", sys.executable, " ".join(sys.argv), None, 1) > 32 and sys.exit()
def request_uac_or_exit():
ctypes.windll.shell32.IsUserAnAdmin() or (ctypes.windll.shell32.ShellExecuteW(
None, "runas", sys.executable, " ".join(sys.argv), None, 1) > 32, sys.exit())

If your script always requires an Administrator's privileges then:
runas /user:Administrator "python your_script.py"

Related

How to program hotstrings in python like in autohotkey

I want to make hotstrings in python that converts one word when typed into another after some processing, since AHK is very limiting when it comes to determining which word to type. Right now, I am using a hotstring in ahk that runs code on the command line that runs a python script with the word that I typed as arguments. Then I use pyautogui to type the word. However, this is very slow and does not work when typing at speed. I'm looking for a way to do this all with python and without ahk, but I have not found a way to do hotstrings in python. For example, every time I type the word "test" it replaces it with "testing." Thanks for your help. I'm running the latest version of Python and Windows 10 if that is useful to anyone by the way.
(if you want to process it as each letter is typed(t,te,tes,test), you should edit your question)
I call my SymPy functions using ahk hotkeys. I register the python script as a COM server and load it using ahk.
I do not notice any latency.
you'll need pywin32, but don't download using pip install pywin32
download from https://github.com/mhammond/pywin32/releases
OR ELSE IT WON'T WORK for AutoHotkeyU64.exe, it will only work for AutoHotkeyU32.exe.
make sure to download amd64, (I downloaded pywin32-300.win-amd64-py3.8.exe)
here's why: how to register a 64bit python COM server
toUppercase COM server.py
class BasicServer:
# list of all method names exposed to COM
_public_methods_ = ["toUppercase"]
#staticmethod
def toUppercase(string):
return string.upper()
if __name__ == "__main__":
import sys
if len(sys.argv) < 2:
print("Error: need to supply arg (""--register"" or ""--unregister"")")
sys.exit(1)
else:
import win32com.server.register
import win32com.server.exception
# this server's CLSID
# NEVER copy the following ID
# Use "print(pythoncom.CreateGuid())" to make a new one.
myClsid="{C70F3BF7-2947-4F87-B31E-9F5B8B13D24F}"
# this server's (user-friendly) program ID
myProgID="Python.stringUppercaser"
import ctypes
def make_sure_is_admin():
try:
if ctypes.windll.shell32.IsUserAnAdmin():
return
except:
pass
exit("YOU MUST RUN THIS AS ADMIN")
if sys.argv[1] == "--register":
make_sure_is_admin()
import pythoncom
import os.path
realPath = os.path.realpath(__file__)
dirName = os.path.dirname(realPath)
nameOfThisFile = os.path.basename(realPath)
nameNoExt = os.path.splitext(nameOfThisFile)[0]
# stuff will be written here
# HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\${myClsid}
# HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{C70F3BF7-2947-4F87-B31E-9F5B8B13D24F}
# and here
# HKEY_LOCAL_MACHINE\SOFTWARE\Classes\${myProgID}
# HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Python.stringUppercaser
win32com.server.register.RegisterServer(
clsid=myClsid,
# I guess this is {fileNameNoExt}.{className}
pythonInstString=nameNoExt + ".BasicServer", #toUppercase COM server.BasicServer
progID=myProgID,
# optional description
desc="return uppercased string",
#we only want the registry key LocalServer32
#we DO NOT WANT InProcServer32: pythoncom39.dll, NO NO NO
clsctx=pythoncom.CLSCTX_LOCAL_SERVER,
#this is needed if this file isn't in PYTHONPATH: it tells regedit which directory this file is located
#this will write HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{C70F3BF7-2947-4F87-B31E-9F5B8B13D24F}\PythonCOMPath : dirName
addnPath=dirName,
)
print("Registered COM server.")
# don't use UseCommandLine(), as it will write InProcServer32: pythoncom39.dll
# win32com.server.register.UseCommandLine(BasicServer)
elif sys.argv[1] == "--unregister":
make_sure_is_admin()
print("Starting to unregister...")
win32com.server.register.UnregisterServer(myClsid, myProgID)
print("Unregistered COM server.")
else:
print("Error: arg not recognized")
you first need to register the python COM server:
first, get your own CLSID: just use a python shell.
import pythoncom
print(pythoncom.CreateGuid())
then, set myClsid to that output
to register:
python "toUppercase COM server.py" --register
to unregister:
python "toUppercase COM server.py" --unregister
hotstring python toUppercase.ahk
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
#SingleInstance, force
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
SetBatchLines, -1
#KeyHistory 0
ListLines Off
#Persistent
#MaxThreadsPerHotkey 4
pythonComServer:=ComObjCreate("Python.stringUppercaser")
; OR
; pythonComServer:=ComObjCreate("{C70F3BF7-2947-4F87-B31E-9F5B8B13D24F}") ;use your own CLSID
; * do not wait for string to end
; C case sensitive
:*:hello world::
savedHotstring:=A_ThisHotkey
;theActualHotstring=savedHotstring[second colon:end of string]
theActualHotstring:=SubStr(savedHotstring, InStr(savedHotstring, ":",, 2) + 1)
send, % pythonComServer.toUppercase(theActualHotstring)
return
f3::Exitapp
you can test the speed of hotstring hello world, it's very fast for me.
Edit def toUppercase(string): to your liking

Executing program via pyvmomi creates a process, but nothing happens after that

I'm studying vCenter 6.5 and community samples help a lot, but in this particular situation I can't figure out, what's going on. The script:
from __future__ import with_statement
import atexit
from tools import cli
from pyVim import connect
from pyVmomi import vim, vmodl
def get_args():
*Boring args parsing works*
return args
def main():
args = get_args()
try:
service_instance = connect.SmartConnectNoSSL(host=args.host,
user=args.user,
pwd=args.password,
port=int(args.port))
atexit.register(connect.Disconnect, service_instance)
content = service_instance.RetrieveContent()
vm = content.searchIndex.FindByUuid(None, args.vm_uuid, True)
creds = vim.vm.guest.NamePasswordAuthentication(
username=args.vm_user, password=args.vm_pwd
)
try:
pm = content.guestOperationsManager.processManager
ps = vim.vm.guest.ProcessManager.ProgramSpec(
programPath=args.path_to_program,
arguments=args.program_arguments
)
res = pm.StartProgramInGuest(vm, creds, ps)
if res > 0:
print "Program executed, PID is %d" % res
except IOError, e:
print e
except vmodl.MethodFault as error:
print "Caught vmodl fault : " + error.msg
return -1
return 0
# Start program
if __name__ == "__main__":
main()
When I execute it in console, it successfully connects to the target virtual machine and prints
Program executed, PID is 2036
In task manager I see process with mentioned PID, it was created by the correct user, but there is no GUI of the process (calc.exe). RMB click does not allow to "Expand" the process.
I suppose, that this process was created with special parameters, maybe in different session.
In addition, I tried to run batch file to check if it actually executes, but the answer is no, batch file does not execute.
Any help, advices, clues would be awesome.
P.S. I tried other scripts and successfully transferred a file to the VM.
P.P.S. Sorry for my English.
Update: All such processes start in session 0.
Have you tried interactiveSession ?
https://github.com/vmware/pyvmomi/blob/master/docs/vim/vm/guest/GuestAuthentication.rst
This boolean argument passed to NamePasswordAuthentication and means the following:
This is set to true if the client wants an interactive session in the guest.

How to create a shortcut in startmenu using setuptools windows installer

I want to create a start menu or Desktop shortcut for my Python windows installer package. I am trying to follow https://docs.python.org/3.4/distutils/builtdist.html#the-postinstallation-script
Here is my script;
import sys
from os.path import dirname, join, expanduser
pyw_executable = sys.executable.replace('python.exe','pythonw.exe')
script_file = join(dirname(pyw_executable), 'Scripts', 'tklsystem-script.py')
w_dir = expanduser(join('~','lsf_files'))
print(sys.argv)
if sys.argv[1] == '-install':
print('Creating Shortcut')
create_shortcut(
target=pyw_executable,
description='A program to work with L-System Equations',
filename='L-System Tool',
arguments=script_file,
workdir=wdir
)
I also specified this script in scripts setup option, as indicated by aforementioned docs.
Here is the command I use to create my installer;
python setup.py bdist_wininst --install-script tklsystem-post-install.py
After I install my package using created windows installer, I can't find where my shorcut is created, nor I can confirm whether my script run or not?
How can I make setuptools generated windows installer to create desktop or start menu shortcuts?
Like others have commented here and elsewhere the support functions don't seem to work at all (at least not with setuptools). After a good day's worth of searching through various resources I found a way to create at least the Desktop shortcut. I'm sharing my solution (basically an amalgam of code I found here and here). I should add that my case is slightly different from yasar's, because it creates a shortcut to an installed package (i.e. an .exe file in Python's Scripts directory) instead of a script.
In short, I added a post_install function to my setup.py, and then used the Python extensions for Windows to create the shortcut. The location of the Desktop folder is read from the Windows registry (there are other methods for this, but they can be unreliable if the Desktop is at a non-standard location).
#!/usr/bin/env python
import os
import sys
import sysconfig
if sys.platform == 'win32':
from win32com.client import Dispatch
import winreg
def get_reg(name,path):
# Read variable from Windows Registry
# From https://stackoverflow.com/a/35286642
try:
registry_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, path, 0,
winreg.KEY_READ)
value, regtype = winreg.QueryValueEx(registry_key, name)
winreg.CloseKey(registry_key)
return value
except WindowsError:
return None
def post_install():
# Creates a Desktop shortcut to the installed software
# Package name
packageName = 'mypackage'
# Scripts directory (location of launcher script)
scriptsDir = sysconfig.get_path('scripts')
# Target of shortcut
target = os.path.join(scriptsDir, packageName + '.exe')
# Name of link file
linkName = packageName + '.lnk'
# Read location of Windows desktop folder from registry
regName = 'Desktop'
regPath = r'Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders'
desktopFolder = os.path.normpath(get_reg(regName,regPath))
# Path to location of link file
pathLink = os.path.join(desktopFolder, linkName)
shell = Dispatch('WScript.Shell')
shortcut = shell.CreateShortCut(pathLink)
shortcut.Targetpath = target
shortcut.WorkingDirectory = scriptsDir
shortcut.IconLocation = target
shortcut.save()
setup(name='mypackage',
...,
...)
if sys.argv[1] == 'install' and sys.platform == 'win32':
post_install()
Here's a link to a full setup script in which I used this:
https://github.com/KBNLresearch/iromlab/blob/master/setup.py
If you want to confirm whether the script is running or not, you can print to a file instead of the console. Looks like text you print to console in the post-install script won't show up.
Try this:
import sys
from os.path import expanduser, join
pyw_executable = join(sys.prefix, "pythonw.exe")
shortcut_filename = "L-System Toolsss.lnk"
working_dir = expanduser(join('~','lsf_files'))
script_path = join(sys.prefix, "Scripts", "tklsystem-script.py")
if sys.argv[1] == '-install':
# Log output to a file (for test)
f = open(r"C:\test.txt",'w')
print('Creating Shortcut', file=f)
# Get paths to the desktop and start menu
desktop_path = get_special_folder_path("CSIDL_COMMON_DESKTOPDIRECTORY")
startmenu_path = get_special_folder_path("CSIDL_COMMON_STARTMENU")
# Create shortcuts.
for path in [desktop_path, startmenu_path]:
create_shortcut(pyw_executable,
"A program to work with L-System Equations",
join(path, shortcut_filename),
script_path,
working_dir)
At least with Python 3.6.5, 32bit on Windows, setuptools does work for this. But based on the accepted answer, by trial and error I found some issues that may have caused your script to fail to do what you wanted.
create_shortcut does not accept keyword arguments, only positional, so its usage in your code is invalid
You must add a .lnk extension for Windows to recognise the shortcut
I found sys.executable will be the name of the installer executable, not the python executable
As mentioned, you can't see stdout or stderr so you might want to log to a text file. I would suggest also redirecting sys.stdout and sys.stderr to the log file.
(Maybe not relevant) as mentioned in this question there appears to be a bug with the version string generated by bdist_wininst. I used the hexediting hack from an answer there to work around this. The location in the answer is not the same, you have to find the -32 yourself.
Full example script:
import sys
import os
import datetime
global datadir
datadir = os.path.join(get_special_folder_path("CSIDL_APPDATA"), "mymodule")
def main(argv):
if "-install" in argv:
desktop = get_special_folder_path("CSIDL_DESKTOPDIRECTORY")
print("Desktop path: %s" % repr(desktop))
if not os.path.exists(datadir):
os.makedirs(datadir)
dir_created(datadir)
print("Created data directory: %s" % repr(datadir))
else:
print("Data directory already existed at %s" % repr(datadir))
shortcut = os.path.join(desktop, "MyModule.lnk")
if os.path.exists(shortcut):
print("Remove existing shortcut at %s" % repr(shortcut))
os.unlink(shortcut)
print("Creating shortcut at %s...\n" % shortcut)
create_shortcut(
r'C:\Python36\python.exe',
"MyModuleScript",
shortcut,
"",
datadir)
file_created(shortcut)
print("Successfull!")
elif "-remove" in sys.argv:
print("Removing...")
pass
if __name__ == "__main__":
logfile = r'C:\mymodule_install.log' # Fallback location
if os.path.exists(datadir):
logfile = os.path.join(datadir, "install.log")
elif os.environ.get("TEMP") and os.path.exists(os.environ.get("TEMP"),""):
logfile = os.path.join(os.environ.get("TEMP"), "mymodule_install.log")
with open(logfile, 'a+') as f:
f.write("Opened\r\n")
f.write("Ran %s %s at %s" % (sys.executable, " ".join(sys.argv), datetime.datetime.now().isoformat()))
sys.stdout = f
sys.stderr = f
try:
main(sys.argv)
except Exception as e:
raise
f.close()
sys.exit(0)
UPD: on an off chance that the client machine has pywin32 installed, we try in-process creation first. Somewhat cleaner that way.
Here is another take. This assumes the package is called myapp, and that also becomes the executable that you want a shortcut to. Substitute your own package name and your own shortcut text.
Uses a Windows Scripting Host COM class - in process if possible, inside a Powershell command line as a subprocess if not. Tested on Python 3.6+.
from setuptools import setup
from setuptools.command.install import install
import platform, sys, os, site
from os import path, environ
def create_shortcut_under(root, exepath):
# Root is an env variable name -
# either ALLUSERSPROFILE for the all users' Start menu,
# or APPDATA for the current user specific one
profile = environ[root]
linkpath = path.join(profile, "Microsoft", "Windows", "Start Menu", "Programs", "My Python app.lnk")
try:
from win32com.client import Dispatch
from pywintypes import com_error
try:
sh = Dispatch('WScript.Shell')
link = sh.CreateShortcut(linkpath)
link.TargetPath = exepath
link.Save()
return True
except com_error:
return False
except ImportError:
import subprocess
s = "$s=(New-Object -COM WScript.Shell).CreateShortcut('" + linkpath + "');$s.TargetPath='" + exepath + "';$s.Save()"
return subprocess.call(['powershell', s], stdout = subprocess.DEVNULL, stderr = subprocess.DEVNULL) == 0
def create_shortcut(inst):
try:
exepath = path.join(path.dirname(sys.executable), "Scripts", "myapp.exe")
if not path.exists(exepath):
# Support for "pip install --user"
exepath = path.join(path.dirname(site.getusersitepackages()), "Scripts", "myapp.exe")
# If can't modify the global menu, fall back to the
# current user's one
if not create_shortcut_under('ALLUSERSPROFILE', exepath):
create_shortcut_under('APPDATA', exepath)
except:
pass
class my_install(install):
def run(self):
install.run(self)
if platform.system() == 'Windows':
create_shortcut(self)
#...
setup(
#...
cmdclass={'install': my_install},
entry_points={"gui_scripts": ["myapp = myapp.__main__:main"]},

How can I change my desktop background with python?

How can I change my desktop background with python?
I want to do it in both Windows and Linux.
On Windows with python2.5 or higher, use ctypes to load user32.dll and call SystemParametersInfo() with SPI_SETDESKWALLPAPER action.
For example:
import ctypes
SPI_SETDESKWALLPAPER = 20
ctypes.windll.user32.SystemParametersInfoA(SPI_SETDESKWALLPAPER, 0, "image.jpg" , 0)
For Python3.5, SystemParametersInfoA doesn't work. Use SystemParametersInfoW.
import ctypes
ctypes.windll.user32.SystemParametersInfoW(20, 0, "absolute path" , 0)
I use the following method in one of my initial projects:
def set_wallpaper(self,file_loc, first_run):
# Note: There are two common Linux desktop environments where
# I have not been able to set the desktop background from
# command line: KDE, Enlightenment
desktop_env = self.get_desktop_environment()
try:
if desktop_env in ["gnome", "unity", "cinnamon"]:
uri = "'file://%s'" % file_loc
try:
SCHEMA = "org.gnome.desktop.background"
KEY = "picture-uri"
gsettings = Gio.Settings.new(SCHEMA)
gsettings.set_string(KEY, uri)
except:
args = ["gsettings", "set", "org.gnome.desktop.background", "picture-uri", uri]
subprocess.Popen(args)
elif desktop_env=="mate":
try: # MATE >= 1.6
# info from http://wiki.mate-desktop.org/docs:gsettings
args = ["gsettings", "set", "org.mate.background", "picture-filename", "'%s'" % file_loc]
subprocess.Popen(args)
except: # MATE < 1.6
# From https://bugs.launchpad.net/variety/+bug/1033918
args = ["mateconftool-2","-t","string","--set","/desktop/mate/background/picture_filename",'"%s"' %file_loc]
subprocess.Popen(args)
elif desktop_env=="gnome2": # Not tested
# From https://bugs.launchpad.net/variety/+bug/1033918
args = ["gconftool-2","-t","string","--set","/desktop/gnome/background/picture_filename", '"%s"' %file_loc]
subprocess.Popen(args)
## KDE4 is difficult
## see http://blog.zx2c4.com/699 for a solution that might work
elif desktop_env in ["kde3", "trinity"]:
# From http://ubuntuforums.org/archive/index.php/t-803417.html
args = 'dcop kdesktop KBackgroundIface setWallpaper 0 "%s" 6' % file_loc
subprocess.Popen(args,shell=True)
elif desktop_env=="xfce4":
#From http://www.commandlinefu.com/commands/view/2055/change-wallpaper-for-xfce4-4.6.0
if first_run:
args0 = ["xfconf-query", "-c", "xfce4-desktop", "-p", "/backdrop/screen0/monitor0/image-path", "-s", file_loc]
args1 = ["xfconf-query", "-c", "xfce4-desktop", "-p", "/backdrop/screen0/monitor0/image-style", "-s", "3"]
args2 = ["xfconf-query", "-c", "xfce4-desktop", "-p", "/backdrop/screen0/monitor0/image-show", "-s", "true"]
subprocess.Popen(args0)
subprocess.Popen(args1)
subprocess.Popen(args2)
args = ["xfdesktop","--reload"]
subprocess.Popen(args)
elif desktop_env=="razor-qt": #TODO: implement reload of desktop when possible
if first_run:
desktop_conf = configparser.ConfigParser()
# Development version
desktop_conf_file = os.path.join(self.get_config_dir("razor"),"desktop.conf")
if os.path.isfile(desktop_conf_file):
config_option = r"screens\1\desktops\1\wallpaper"
else:
desktop_conf_file = os.path.join(self.get_home_dir(),".razor/desktop.conf")
config_option = r"desktops\1\wallpaper"
desktop_conf.read(os.path.join(desktop_conf_file))
try:
if desktop_conf.has_option("razor",config_option): #only replacing a value
desktop_conf.set("razor",config_option,file_loc)
with codecs.open(desktop_conf_file, "w", encoding="utf-8", errors="replace") as f:
desktop_conf.write(f)
except:
pass
else:
#TODO: reload desktop when possible
pass
elif desktop_env in ["fluxbox","jwm","openbox","afterstep"]:
#http://fluxbox-wiki.org/index.php/Howto_set_the_background
# used fbsetbg on jwm too since I am too lazy to edit the XML configuration
# now where fbsetbg does the job excellent anyway.
# and I have not figured out how else it can be set on Openbox and AfterSTep
# but fbsetbg works excellent here too.
try:
args = ["fbsetbg", file_loc]
subprocess.Popen(args)
except:
sys.stderr.write("ERROR: Failed to set wallpaper with fbsetbg!\n")
sys.stderr.write("Please make sre that You have fbsetbg installed.\n")
elif desktop_env=="icewm":
# command found at http://urukrama.wordpress.com/2007/12/05/desktop-backgrounds-in-window-managers/
args = ["icewmbg", file_loc]
subprocess.Popen(args)
elif desktop_env=="blackbox":
# command found at http://blackboxwm.sourceforge.net/BlackboxDocumentation/BlackboxBackground
args = ["bsetbg", "-full", file_loc]
subprocess.Popen(args)
elif desktop_env=="lxde":
args = "pcmanfm --set-wallpaper %s --wallpaper-mode=scaled" % file_loc
subprocess.Popen(args,shell=True)
elif desktop_env=="windowmaker":
# From http://www.commandlinefu.com/commands/view/3857/set-wallpaper-on-windowmaker-in-one-line
args = "wmsetbg -s -u %s" % file_loc
subprocess.Popen(args,shell=True)
## NOT TESTED BELOW - don't want to mess things up ##
#elif desktop_env=="enlightenment": # I have not been able to make it work on e17. On e16 it would have been something in this direction
# args = "enlightenment_remote -desktop-bg-add 0 0 0 0 %s" % file_loc
# subprocess.Popen(args,shell=True)
#elif desktop_env=="windows": #Not tested since I do not run this on Windows
# #From https://stackoverflow.com/questions/1977694/change-desktop-background
# import ctypes
# SPI_SETDESKWALLPAPER = 20
# ctypes.windll.user32.SystemParametersInfoA(SPI_SETDESKWALLPAPER, 0, file_loc , 0)
#elif desktop_env=="mac": #Not tested since I do not have a mac
# #From https://stackoverflow.com/questions/431205/how-can-i-programatically-change-the-background-in-mac-os-x
# try:
# from appscript import app, mactypes
# app('Finder').desktop_picture.set(mactypes.File(file_loc))
# except ImportError:
# #import subprocess
# SCRIPT = """/usr/bin/osascript<<END
# tell application "Finder" to
# set desktop picture to POSIX file "%s"
# end tell
# END"""
# subprocess.Popen(SCRIPT%file_loc, shell=True)
else:
if first_run: #don't spam the user with the same message over and over again
sys.stderr.write("Warning: Failed to set wallpaper. Your desktop environment is not supported.")
sys.stderr.write("You can try manually to set Your wallpaper to %s" % file_loc)
return False
return True
except:
sys.stderr.write("ERROR: Failed to set wallpaper. There might be a bug.\n")
return False
def get_config_dir(self, app_name=APP_NAME):
if "XDG_CONFIG_HOME" in os.environ:
confighome = os.environ['XDG_CONFIG_HOME']
elif "APPDATA" in os.environ: # On Windows
confighome = os.environ['APPDATA']
else:
try:
from xdg import BaseDirectory
confighome = BaseDirectory.xdg_config_home
except ImportError: # Most likely a Linux/Unix system anyway
confighome = os.path.join(self.get_home_dir(),".config")
configdir = os.path.join(confighome,app_name)
return configdir
def get_home_dir(self):
if sys.platform == "cygwin":
home_dir = os.getenv('HOME')
else:
home_dir = os.getenv('USERPROFILE') or os.getenv('HOME')
if home_dir is not None:
return os.path.normpath(home_dir)
else:
raise KeyError("Neither USERPROFILE or HOME environment variables set.")
The get_desktop_environment method has been posted in another thread.
On a gnome desktop, you usually do this with gconf, either directly calling gconftool or using the gconf python module. The latter is in the link given by unutbu. The first method could be done like this.
import commands
command = "gconftool-2 --set /desktop/gnome/background/picture_filename --type string '/path/to/file.jpg'"
status, output = commands.getstatusoutput(command) # status=0 if success
In gnome, it is probably preferable to use the python binding of gconf directly:
import gconf
conf = gconf.client_get_default()
conf.set_string('/desktop/gnome/background/picture_filename','/path/to/filename.jpg')
On windows, you will need some trickery with pywin32, and the windows API, on 'linux' the answer will depend on which desktop is running - KDE, Gnome, or something more exotic. Under KDE (and maybe Gnome) you can probably send a message using D-Bus, which you could do without including any new libraries by using the command line tool dbus-send.
The other option would be to set the desktop wallpaper to a file which you then edit / replace from python - but this will probably only result in a change when the user logs in.
Firstly, import ctypes: it gives you access to windows components such as the screensaver, wallpapers, etc.
Then call
ctypes.windll.user32.SystemParametersInfoA(20, 0, the_complete_path_of_your_image, 0)
Make sure the path is the complete path of your image, not just the path from the active directory
There is a difference what SystemParametersInfo method to be called based on what if you are running on 64 bit or 32 bit OS. For 64 bit you have to use SystemParametersInfoW (Unicode) and for 32 bit SystemParametersInfoA (ANSI)
import struct
import ctypes
SPI_SETDESKWALLPAPER = 20
WALLPAPER_PATH = 'C:\\your_file_name.jpg'
def is_64_windows():
"""Find out how many bits is OS. """
return struct.calcsize('P') * 8 == 64
def get_sys_parameters_info():
"""Based on if this is 32bit or 64bit returns correct version of SystemParametersInfo function. """
return ctypes.windll.user32.SystemParametersInfoW if is_64_windows() \
else ctypes.windll.user32.SystemParametersInfoA
def change_wallpaper():
sys_parameters_info = get_sys_parameters_info()
r = sys_parameters_info(SPI_SETDESKWALLPAPER, 0, WALLPAPER_PATH, 3)
# When the SPI_SETDESKWALLPAPER flag is used,
# SystemParametersInfo returns TRUE
# unless there is an error (like when the specified file doesn't exist).
if not r:
print(ctypes.WinError())
change_wallpaper()
import ctypes,win32con
def getWallpaper():
ubuf = ctypes.create_unicode_buffer(512)
ctypes.windll.user32.SystemParametersInfoW(win32con.SPI_GETDESKWALLPAPER,len(ubuf),ubuf,0)
return ubuf.value
def setWallpaper(path):
changed = win32con.SPIF_UPDATEINIFILE | win32con.SPIF_SENDCHANGE
ctypes.windll.user32.SystemParametersInfoW(win32con.SPI_SETDESKWALLPAPER,0,path,changed)
Alternatively: (with SystemParametersInfoA)
def getWallpaper():
sbuf = ctypes.create_string_buffer(512) # ctypes.c_buffer(512)
ctypes.windll.user32.SystemParametersInfoA(win32con.SPI_GETDESKWALLPAPER,len(sbuf),sbuf,0)
return sbuf.value
def setWallpaper(path):
changed = win32con.SPIF_UPDATEINIFILE | win32con.SPIF_SENDCHANGE
ctypes.windll.user32.SystemParametersInfoA(win32con.SPI_SETDESKWALLPAPER,0,path.encode(),changed) # "".encode() = b""
Arguments are:
SystemParametersInfo(SetOrGet, GetBufferSize, SetBufferOrGetBuffer, SetChange)
The path has to be absolute, so if you're using something relative to your script, do:
path = os.path.abspath(path)
To see more stuff you can do with SystemParametersInfo, see the docs.
(near the bottom there's an example to change the mouse speed)
P.S. There are many answers already here, but they're leaving out the broadcasting you're supposed to do. Sure it works without it, but it's bad practice not to use it properly.
P.P.S And they only gave hard coded values, rather than the variables they come from.
Also note, i use 512 characters for the buffer size when getting the path, just to be more safe since paths might exceed 256. I doubt anyone will have paths as long as that though.
One more note. I've only tested the above examples in Python 3, but i don't think SystemParametersInfoA needs the .encode() in Python 2. (they updated strings in Python 3 to unicode i believe) The string in SystemParametersInfoW may need converting for Python 2.
I read all the answers and after searching for a while i found a easier solution.
Install the module named py-wallpaper.
pip install py-wallpaper
Import the module.
from wallpaper import set_wallpaper, get_wallpaper
set the wallpaper using set walpaper
set_wallpaper("location/to/image.jpg")
get the current wallpaper's path using get wallpaper
print(get_wallpaper())
thanks.
changing the background image of desktop
import ctypes
import os
SPI_SETDESKWALLPAPER = 20
ctypes.windll.user32.SystemParametersInfoA(SPI_SETDESKWALLPAPER, 0, 'your image path', 3)
#'C:\\Users\\Public\\Pictures\\abc.jpg'
it worked fine for me. windows10, python27
On Windows with python2.5 or higher, use ctypes to load user32.dll and call
import ctypes
ctypes.windll.user32.SystemParametersInfoW(20,0,"Path_wallpaper", 0)
speak("Background changed succesfully")
Just adding a small precision to ShivaGuntuku 's post :
In python 3 you should replace the 'A' by a 'W' in SytemParametersInfoA. Small exemple to change your desktop background in windows10 with python 3 :
import ctypes
import os
SPI_SETDESKWALLPAPER = 20
ctypes.windll.user32.SystemParametersInfoW(
SPI_SETDESKWALLPAPER, 0, 'C:\\Users\\godet\\OneDrive\\Images\\breaker_wall.jpg', 0)
this works for me
import ctypes
ctypes.windll.user32.SystemParametersInfoW(20,0,path:os.PathLike,3)
You can use this library PyWallpaper, worked for me on mac also.
To install type pip install PyWallpaper.
And then to change/set your wallpaper -
from PyWallpaper import change_wallpaper
change_wallpaper("/some_path/sample.jpg")

Request UAC elevation from within a Python script?

I want my Python script to copy files on Vista. When I run it from a normal cmd.exe window, no errors are generated, yet the files are NOT copied. If I run cmd.exe "as administator" and then run my script, it works fine.
This makes sense since User Account Control (UAC) normally prevents many file system actions.
Is there a way I can, from within a Python script, invoke a UAC elevation request (those dialogs that say something like "such and such app needs admin access, is this OK?")
If that's not possible, is there a way my script can at least detect that it is not elevated so it can fail gracefully?
As of 2017, an easy method to achieve this is the following:
import ctypes, sys
def is_admin():
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
if is_admin():
# Code of your program here
else:
# Re-run the program with admin rights
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1)
If you are using Python 2.x, then you should replace the last line for:
ctypes.windll.shell32.ShellExecuteW(None, u"runas", unicode(sys.executable), unicode(" ".join(sys.argv)), None, 1)
Also note that if you converted you python script into an executable file (using tools like py2exe, cx_freeze, pyinstaller) then you should use sys.argv[1:] instead of sys.argv in the fourth parameter.
Some of the advantages here are:
No external libraries required. It only uses ctypes and sys from standard library.
Works on both Python 2 and Python 3.
There is no need to modify the file resources nor creating a manifest file.
If you don't add code below if/else statement, the code won't ever be executed twice.
You can get the return value of the API call in the last line and take an action if it fails (code <= 32). Check possible return values here.
You can change the display method of the spawned process modifying the sixth parameter.
Documentation for the underlying ShellExecute call is here.
It took me a little while to get dguaraglia's answer working, so in the interest of saving others time, here's what I did to implement this idea:
import os
import sys
import win32com.shell.shell as shell
ASADMIN = 'asadmin'
if sys.argv[-1] != ASADMIN:
script = os.path.abspath(sys.argv[0])
params = ' '.join([script] + sys.argv[1:] + [ASADMIN])
shell.ShellExecuteEx(lpVerb='runas', lpFile=sys.executable, lpParameters=params)
sys.exit(0)
It seems there's no way to elevate the application privileges for a while for you to perform a particular task. Windows needs to know at the start of the program whether the application requires certain privileges, and will ask the user to confirm when the application performs any tasks that need those privileges. There are two ways to do this:
Write a manifest file that tells Windows the application might require some privileges
Run the application with elevated privileges from inside another program
This two articles explain in much more detail how this works.
What I'd do, if you don't want to write a nasty ctypes wrapper for the CreateElevatedProcess API, is use the ShellExecuteEx trick explained in the Code Project article (Pywin32 comes with a wrapper for ShellExecute). How? Something like this:
When your program starts, it checks if it has Administrator privileges, if it doesn't it runs itself using the ShellExecute trick and exits immediately, if it does, it performs the task at hand.
As you describe your program as a "script", I suppose that's enough for your needs.
Cheers.
Just adding this answer in case others are directed here by Google Search as I was.
I used the elevate module in my Python script and the script executed with Administrator Privileges in Windows 10.
https://pypi.org/project/elevate/
The following example builds on MARTIN DE LA FUENTE SAAVEDRA's excellent work and accepted answer. In particular, two enumerations are introduced. The first allows for easy specification of how an elevated program is to be opened, and the second helps when errors need to be easily identified. Please note that if you want all command line arguments passed to the new process, sys.argv[0] should probably be replaced with a function call: subprocess.list2cmdline(sys.argv).
#! /usr/bin/env python3
import ctypes
import enum
import subprocess
import sys
# Reference:
# msdn.microsoft.com/en-us/library/windows/desktop/bb762153(v=vs.85).aspx
# noinspection SpellCheckingInspection
class SW(enum.IntEnum):
HIDE = 0
MAXIMIZE = 3
MINIMIZE = 6
RESTORE = 9
SHOW = 5
SHOWDEFAULT = 10
SHOWMAXIMIZED = 3
SHOWMINIMIZED = 2
SHOWMINNOACTIVE = 7
SHOWNA = 8
SHOWNOACTIVATE = 4
SHOWNORMAL = 1
class ERROR(enum.IntEnum):
ZERO = 0
FILE_NOT_FOUND = 2
PATH_NOT_FOUND = 3
BAD_FORMAT = 11
ACCESS_DENIED = 5
ASSOC_INCOMPLETE = 27
DDE_BUSY = 30
DDE_FAIL = 29
DDE_TIMEOUT = 28
DLL_NOT_FOUND = 32
NO_ASSOC = 31
OOM = 8
SHARE = 26
def bootstrap():
if ctypes.windll.shell32.IsUserAnAdmin():
main()
else:
# noinspection SpellCheckingInspection
hinstance = ctypes.windll.shell32.ShellExecuteW(
None,
'runas',
sys.executable,
subprocess.list2cmdline(sys.argv),
None,
SW.SHOWNORMAL
)
if hinstance <= 32:
raise RuntimeError(ERROR(hinstance))
def main():
# Your Code Here
print(input('Echo: '))
if __name__ == '__main__':
bootstrap()
Recognizing this question was asked years ago, I think a more elegant solution is offered on github by frmdstryr using his module pywinutils:
Excerpt:
import pythoncom
from win32com.shell import shell,shellcon
def copy(src,dst,flags=shellcon.FOF_NOCONFIRMATION):
""" Copy files using the built in Windows File copy dialog
Requires absolute paths. Does NOT create root destination folder if it doesn't exist.
Overwrites and is recursive by default
#see http://msdn.microsoft.com/en-us/library/bb775799(v=vs.85).aspx for flags available
"""
# #see IFileOperation
pfo = pythoncom.CoCreateInstance(shell.CLSID_FileOperation,None,pythoncom.CLSCTX_ALL,shell.IID_IFileOperation)
# Respond with Yes to All for any dialog
# #see http://msdn.microsoft.com/en-us/library/bb775799(v=vs.85).aspx
pfo.SetOperationFlags(flags)
# Set the destionation folder
dst = shell.SHCreateItemFromParsingName(dst,None,shell.IID_IShellItem)
if type(src) not in (tuple,list):
src = (src,)
for f in src:
item = shell.SHCreateItemFromParsingName(f,None,shell.IID_IShellItem)
pfo.CopyItem(item,dst) # Schedule an operation to be performed
# #see http://msdn.microsoft.com/en-us/library/bb775780(v=vs.85).aspx
success = pfo.PerformOperations()
# #see sdn.microsoft.com/en-us/library/bb775769(v=vs.85).aspx
aborted = pfo.GetAnyOperationsAborted()
return success is None and not aborted
This utilizes the COM interface and automatically indicates that admin privileges are needed with the familiar dialog prompt that you would see if you were copying into a directory where admin privileges are required and also provides the typical file progress dialog during the copy operation.
This may not completely answer your question but you could also try using the Elevate Command Powertoy in order to run the script with elevated UAC privileges.
http://technet.microsoft.com/en-us/magazine/2008.06.elevation.aspx
I think if you use it it would look like 'elevate python yourscript.py'
You can make a shortcut somewhere and as the target use:
python yourscript.py
then under properties and advanced select run as administrator.
When the user executes the shortcut it will ask them to elevate the application.
A variation on Jorenko's work above allows the elevated process to use the same console (but see my comment below):
def spawn_as_administrator():
""" Spawn ourself with administrator rights and wait for new process to exit
Make the new process use the same console as the old one.
Raise Exception() if we could not get a handle for the new re-run the process
Raise pywintypes.error() if we could not re-spawn
Return the exit code of the new process,
or return None if already running the second admin process. """
#pylint: disable=no-name-in-module,import-error
import win32event, win32api, win32process
import win32com.shell.shell as shell
if '--admin' in sys.argv:
return None
script = os.path.abspath(sys.argv[0])
params = ' '.join([script] + sys.argv[1:] + ['--admin'])
SEE_MASK_NO_CONSOLE = 0x00008000
SEE_MASK_NOCLOSE_PROCESS = 0x00000040
process = shell.ShellExecuteEx(lpVerb='runas', lpFile=sys.executable, lpParameters=params, fMask=SEE_MASK_NO_CONSOLE|SEE_MASK_NOCLOSE_PROCESS)
hProcess = process['hProcess']
if not hProcess:
raise Exception("Could not identify administrator process to install drivers")
# It is necessary to wait for the elevated process or else
# stdin lines are shared between 2 processes: they get one line each
INFINITE = -1
win32event.WaitForSingleObject(hProcess, INFINITE)
exitcode = win32process.GetExitCodeProcess(hProcess)
win32api.CloseHandle(hProcess)
return exitcode
This is mostly an upgrade to Jorenko's answer, that allows to use parameters with spaces in Windows, but should also work fairly well on Linux :)
Also, will work with cx_freeze or py2exe since we don't use __file__ but sys.argv[0] as executable
[EDIT]
Disclaimer: The code in this post is outdated.
I have published the elevation code as a python package.
Install with pip install command_runner
Usage:
from command_runner.elevate import elevate
def main():
"""My main function that should be elevated"""
print("Who's the administrator, now ?")
if __name__ == '__main__':
elevate(main)
[/EDIT]
import sys,ctypes,platform
def is_admin():
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
raise False
if __name__ == '__main__':
if platform.system() == "Windows":
if is_admin():
main(sys.argv[1:])
else:
# Re-run the program with admin rights, don't use __file__ since py2exe won't know about it
# Use sys.argv[0] as script path and sys.argv[1:] as arguments, join them as lpstr, quoting each parameter or spaces will divide parameters
lpParameters = ""
# Litteraly quote all parameters which get unquoted when passed to python
for i, item in enumerate(sys.argv[0:]):
lpParameters += '"' + item + '" '
try:
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, lpParameters , None, 1)
except:
sys.exit(1)
else:
main(sys.argv[1:])
For one-liners, put the code to where you need UAC.
Request UAC, if failed, keep running:
import ctypes, sys
ctypes.windll.shell32.IsUserAnAdmin() or ctypes.windll.shell32.ShellExecuteW(
None, "runas", sys.executable, " ".join(sys.argv), None, 1) > 32 and exit()
Request UAC, if failed, exit:
import ctypes, sys
ctypes.windll.shell32.IsUserAnAdmin() or (ctypes.windll.shell32.ShellExecuteW(
None, "runas", sys.executable, " ".join(sys.argv), None, 1) > 32, exit())
Function style:
# Created by BaiJiFeiLong#gmail.com at 2022/6/24
import ctypes
import sys
def request_uac_or_skip():
ctypes.windll.shell32.IsUserAnAdmin() or ctypes.windll.shell32.ShellExecuteW(
None, "runas", sys.executable, " ".join(sys.argv), None, 1) > 32 and sys.exit()
def request_uac_or_exit():
ctypes.windll.shell32.IsUserAnAdmin() or (ctypes.windll.shell32.ShellExecuteW(
None, "runas", sys.executable, " ".join(sys.argv), None, 1) > 32, sys.exit())
If your script always requires an Administrator's privileges then:
runas /user:Administrator "python your_script.py"

Categories