The ShellExecute API call will spawn a new process, it won't elevate permissions for the current process running.
Let's analyze this code snippet:
if is_admin():
main()
else:
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1)
When you first run this Python script without privileges, this initial process will jump to the last line of the code because is_admin returns False. Once there, the UAC prompt is displayed.
- If the UAC prompt is accepted, then a completely new process (with different process ID) is created and the code is executed again (from the beginning), but this time with admin privileges. Now
is_adminshould returnTrueandmainshould be called. - If the UAC prompt is rejected, no new process is created.
Regardless of the UAC response, the initial process will get the return code back, but its privileges will remain unaltered.
If you want to try this yourself, add an input() at the end of the file and you should be able to see two different windows after accepting the UAC prompt.
To avoid having your code being executed twice be sure to keep everything inside the main function. If you want to take an action based on the return code, this only makes sense for failures (code <= 32). If the return code is successfull (> 32), then the process should end gracefully and let the new spawned process do its job.
python - How do I run a script with elevated UAC permissions using ctypes? - Stack Overflow
python ctypes, call user32 and kernel32 func with arguments - Stack Overflow
Request UAC Elevation From Within Python Script With ctypes.windll.shell32.ShellExecuteW - Stack Overflow
python - Importing ctypes.windll.shell32.IsUserAnAdmin gives ImportError - Stack Overflow
The ShellExecute API call will spawn a new process, it won't elevate permissions for the current process running.
Let's analyze this code snippet:
if is_admin():
main()
else:
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1)
When you first run this Python script without privileges, this initial process will jump to the last line of the code because is_admin returns False. Once there, the UAC prompt is displayed.
- If the UAC prompt is accepted, then a completely new process (with different process ID) is created and the code is executed again (from the beginning), but this time with admin privileges. Now
is_adminshould returnTrueandmainshould be called. - If the UAC prompt is rejected, no new process is created.
Regardless of the UAC response, the initial process will get the return code back, but its privileges will remain unaltered.
If you want to try this yourself, add an input() at the end of the file and you should be able to see two different windows after accepting the UAC prompt.
To avoid having your code being executed twice be sure to keep everything inside the main function. If you want to take an action based on the return code, this only makes sense for failures (code <= 32). If the return code is successfull (> 32), then the process should end gracefully and let the new spawned process do its job.
import ctypes
import sys
import platform
def admin() -> "Admin Bool":
"""Requests UAC Admin on Windows with a prompt"""
if platform.system() == "Windows":
ctypes.windll.shell32.ShellExecuteW(
None,
'runas',
sys.executable,
' '.join(sys.argv),
None,
None
)
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
else:
raise OSError("admin() only works for windows.")
This will request admin with an admin prompt.
If True is returned, then admin has been granted by the user. Else, False will be returned.
You should read the API decscription. Here's the link for GetConsoleTitle: https://learn.microsoft.com/en-us/windows/console/getconsoletitle
DWORD WINAPI GetConsoleTitle(
_Out_ LPTSTR lpConsoleTitle,
_In_ DWORD nSize
);
Update: Here's a short demo of getting console window title:
import ctypes
MAX_BUFFER = 260
title_text_buffer = (ctypes.c_char * MAX_BUFFER)()
res = ctypes.windll.kernel32.GetConsoleTitleA(title_text_buffer, MAX_BUFFER)
title_text = title_text_buffer.value
print title_text
Tiny update to above code:
>>> import ctypes
>>> MAX_LEN = 256
>>> buffer_ = ctypes.create_unicode_buffer(MAX_LEN)
>>> ctypes.windll.kernel32.GetConsoleTitleW(buffer_, MAX_LEN)
5
>>> buffer_.value
'Command Prompt - python'
Sure, it's located in pythoncom and shell for constants, for example:
from win32com.shell import shell
import pythoncom
# create an instance of IFileOperation
fo = pythoncom.CoCreateInstance(shell.CLSID_FileOperation, None, pythoncom.CLSCTX_ALL, shell.IID_IFileOperation)
# here you can use SetOperationFlags, progress Sinks, etc.
# create an instance of IShellItem for the source item
item1 = shell.SHCreateItemFromParsingName("c:\\temp\\source.txt", None, shell.IID_IShellItem)
# create an instance of IShellItem for the target folder
folder = shell.SHCreateItemFromParsingName("c:\\another", None, shell.IID_IShellItem)
# queue the copy operation
fo.CopyItem(item1, folder, "new name.txt", None)
# commit
fo.PerformOperations()
This question put me on track, as it shows that COM loading Windows functionality is in fact avialable from ctypes, albeit it requires a bit more work.
The question uses comtypes.GUID as the only (non standard) dependency.
Looking at comtypes itself, it's pure python and uses ctypes (for CoCreateInstance and all else), and the paths to the windows functions needed to load and handle the COM object can be found, specifically -
import ctypes
ctypes.oledll.ole32.CoCreateInstance()
The CLSIDs need to be put explicitly, as in the referred question -
IID_IFileOperation = '{947AAB5F-0A5C-4C13-B4D6-4BF7836FC9F8}'
CLSID_FileOperation = '{3AD05575-8857-4850-9277-11B85BDB8E09}'
All and all, comtypes, which is a small pure python library, seems quite enough for this task, if one doesn't want to tinker with ctypes, paste in GUID or else doesn't mind the dependency.
However, this is fully implementable in ctypes, as proven by comtypes itself, with the caveat of possibly having to add in GUID manually -
from ctypes import *
BYTE, WORD, DWORD = c_byte, c_ushort, c_ulong
_StringFromCLSID = oledll.ole32.StringFromCLSID
_ProgIDFromCLSID = oledll.ole32.ProgIDFromCLSID
_CLSIDFromString = oledll.ole32.CLSIDFromString
_CLSIDFromProgID = oledll.ole32.CLSIDFromProgID
_CoCreateGuid = oledll.ole32.CoCreateGuid
_CoTaskMemFree = windll.ole32.CoTaskMemFree
class GUID(Structure):
_fields_ = [("Data1", DWORD),
("Data2", WORD),
("Data3", WORD),
("Data4", BYTE * 8)]
def __init__(self, name=None):
if name is not None:
_CLSIDFromString(unicode(name), byref(self))
def __repr__(self):
return u'GUID("%s")' % unicode(self)
def __unicode__(self):
p = c_wchar_p()
_StringFromCLSID(byref(self), byref(p))
result = p.value
_CoTaskMemFree(p)
return result
__str__ = __unicode__
def __cmp__(self, other):
if isinstance(other, GUID):
return cmp(bytes(self), bytes(other))
return -1
def __nonzero__(self):
return self != GUID_null
def __eq__(self, other):
return isinstance(other, GUID) and \
bytes(self) == bytes(other)
def __hash__(self):
# We make GUID instances hashable, although they are mutable.
return hash(bytes(self))
def copy(self):
return GUID(unicode(self))
def from_progid(cls, progid):
"""Get guid from progid, ...
"""
if hasattr(progid, "_reg_clsid_"):
progid = progid._reg_clsid_
if isinstance(progid, cls):
return progid
elif isinstance(progid, basestring):
if progid.startswith("{"):
return cls(progid)
inst = cls()
_CLSIDFromProgID(unicode(progid), byref(inst))
return inst
else:
raise TypeError("Cannot construct guid from %r" % progid)
from_progid = classmethod(from_progid)
def as_progid(self):
"Convert a GUID into a progid"
progid = c_wchar_p()
_ProgIDFromCLSID(byref(self), byref(progid))
result = progid.value
_CoTaskMemFree(progid)
return result
def create_new(cls):
"Create a brand new guid"
guid = cls()
_CoCreateGuid(byref(guid))
return guid
create_new = classmethod(create_new)
Hello! I recently started on a function that creates some specified amount of text windows and spreads them around the screen.
The problem is that ctypes.windll.user32.MoveWindows and ctypes.windll.user32.SetWindowPos both not work for some reason.
I also found that the creation of the message box blocks the normal flow of the code, so then I put that creation of the box in a thread, but then you cannot specify the hwnd (handle for the window) for the move functions. I tried just putting it normally, but then it blocks the flow and does not let the move function do their thing.
It currently just stacks the windows diagonally like the old windows glitch, which is also cool, but not the wanted effect.
import time
import ctypes
import random
import threading
def message_box(title, message, count):
try:
values = 0 | 0x10 | 0x40000
def show_box():
screen_width = ctypes.windll.user32.GetSystemMetrics(0)
screen_height = ctypes.windll.user32.GetSystemMetrics(1)
x = random.randint(0, (screen_width - 200))
y = random.randint(0, (screen_height - 100))
print(f"X: {x}, Y: {y}")
hwnd = ctypes.windll.user32.MessageBoxW(0, message, title, values)
ctypes.windll.user32.MoveWindow(hwnd, x, y, 150, 50)
ctypes.windll.user32.SetWindowPos(hwnd, -1, x, y, 0, 0, 0x0001)
for i in range(count):
threading.Thread(target=show_box).start()
time.sleep(0.5)
except Exception as e:
print(f"Error displaying message box: {e}")
if __name__ == '__main__':
message_box("Hello", "This is a test message", 5)Here is my code so far if it is any help:
Is this actually even possible?