Re: [python-win32] taskscheduler: how to tick setting "Run task as soon as possible after scheduled start is missed"
On 1/5/23, Joel Moberg wrote: > Maybe I can adjust the flag in the Trigger object but I have no idea what > flags are available. The following loosely adapts Microsoft's time trigger task example, with commented links to get further information. The property that directly addresses your question is taskDefinition.Settings.StartWhenAvailable. --- import win32com.client from pywintypes import com_error from datetime import datetime, timedelta # Connect a TaskService instance. # https://learn.microsoft.com/windows/win32/taskschd/taskservice service = win32com.client.Dispatch('Schedule.Service') service.Connect() # Create a TaskDefinition. # https://learn.microsoft.com/windows/win32/taskschd/taskdefinition taskDefinition = service.NewTask(0) # Set the task description and author name. # https://learn.microsoft.com/windows/win32/taskschd/registrationinfo taskDefinition.RegistrationInfo.Description = ( "Start notepad at a certain time") taskDefinition.RegistrationInfo.Author = "Author Name" # Enable the task to start after the scheduled time. # https://learn.microsoft.com/windows/win32/taskschd/tasksettings taskDefinition.Settings.Enabled = True taskDefinition.Settings.StartWhenAvailable = True # Add a TimeTrigger. # https://learn.microsoft.com/windows/win32/taskschd/triggercollection # https://learn.microsoft.com/windows/win32/taskschd/timetrigger TASK_TRIGGER_TIME = 1 # The time format is "-MM-DD" "T" "HH:MM:SS" [(+-)"HH:MM"]. For # example, "1980-01-01T00:00:00+00:00". Local time is used if the # optional UTC offset is omitted. TIME_TEMPLATE = "{:%Y-%m-%dT%H:%M:%S}" trigger = taskDefinition.Triggers.Create(TASK_TRIGGER_TIME) trigger.Id = "TimeTriggerId" trigger.Enabled = True trigger.StartBoundary = TIME_TEMPLATE.format( datetime.now() + timedelta(seconds=30)) trigger.EndBoundary = TIME_TEMPLATE.format( datetime.now() + timedelta(minutes=5)) trigger.ExecutionTimeLimit = "PT5M" # 5 minutes # Add an ExecAction. # https://learn.microsoft.com/windows/win32/taskschd/actioncollection # https://learn.microsoft.com/windows/win32/taskschd/execaction TASK_ACTION_EXEC = 0 action = taskDefinition.Actions.Create(TASK_ACTION_EXEC) action.Id = "ExecActionid" action.Path = r"C:\Windows\System32\notepad.exe" # Register the task to run only in the user's interactive session. # https://learn.microsoft.com/windows/win32/taskschd/taskfolder TASK_CREATE_OR_UPDATE = 6 TASK_LOGON_PASSWORD = 1 TASK_LOGON_S4U = 2 # requires admin access TASK_LOGON_INTERACTIVE_TOKEN = 3 TASK_LOGON_GROUP = 4 TASK_LOGON_SERVICE_ACCOUNT = 5 service.GetFolder("\\").RegisterTaskDefinition( "Time Trigger Test", taskDefinition, TASK_CREATE_OR_UPDATE, None, None, TASK_LOGON_INTERACTIVE_TOKEN) ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] Need a value from pywin32
On 6/21/22, Steven Manross wrote: > > class WinStationInformation(ctypes.Structure): > __fields__ = [ > ('ConnectState', ctypes.c_long), > ('WinStationName', ctypes.wintypes.WCHAR), > ('LogonId', ctypes.c_ulong), > ('ConnectTime', ctypes.wintypes.LARGE_INTEGER), > ('DisconnectTime', ctypes.wintypes.LARGE_INTEGER), > ('LastInputTime', ctypes.wintypes.LARGE_INTEGER), > ('LogonTime', ctypes.wintypes.LARGE_INTEGER), > ('Status', ctypes.c_int()), > ('Domain', ctypes.wintypes.WCHAR * (17 + 1)), > ('UserName', ctypes.wintypes.WCHAR * (20 + 1)), > ('CurrentTime', ctypes.wintypes.LARGE_INTEGER), > ] The above definition is incorrect for `WinStationName` and `Status`. Defining the PROTOCOLSTATUS type for `Status` is tedious. Note also that the ctypes attribute to set is `_fields_`, not `__fields__`, so the above struct is actually defined with no fields (i.e. zero size). Here's an example that defines a get_idle_time() function, based on the difference between the current time and the last input time in the session. import ctypes from ctypes import wintypes winsta = ctypes.WinDLL('winsta', use_last_error=True) WINSTATIONNAME_LENGTH = 32 DOMAIN_LENGTH = 17 USERNAME_LENGTH = 20 MAX_THINWIRECACHE = 4 SERVERNAME_CURRENT = None LOGONID_CURRENT = -1 # WINSTATIONINFOCLASS WinStationInformation = 8 # WINSTATIONSTATECLASS State_Active = 0 State_Connected = 1 State_ConnectQuery = 2 State_Shadow = 3 State_Disconnected = 4 State_Idle = 5 State_Listen = 6 State_Reset = 7 State_Down = 8 State_Init = 9 class TSHARE_COUNTERS(ctypes.Structure): __slots__ = () _fields_ = ( ('Reserved', wintypes.ULONG), ) class PROTOCOLCOUNTERS(ctypes.Structure): __slots__ = () class SPECIFIC(ctypes.Union): __slots__ = () _fields_ = ( ('TShareCounters', TSHARE_COUNTERS), ('Reserved', wintypes.ULONG * 100), ) _fields_ = ( ('WdBytes', wintypes.ULONG), ('WdFrames', wintypes.ULONG), ('WaitForOutBuf', wintypes.ULONG), ('Frames', wintypes.ULONG), ('Bytes', wintypes.ULONG), ('CompressedBytes', wintypes.ULONG), ('CompressFlushes', wintypes.ULONG), ('Errors', wintypes.ULONG), ('Timeouts', wintypes.ULONG), ('AsyncFramingError', wintypes.ULONG), ('AsyncOverrunError', wintypes.ULONG), ('AsyncOverflowError', wintypes.ULONG), ('AsyncParityError', wintypes.ULONG), ('TdErrors', wintypes.ULONG), ('ProtocolType', wintypes.USHORT), ('Length', wintypes.USHORT), ('Specific', SPECIFIC), ) class THINWIRECACHE (ctypes.Structure): __slots__ = () _fields_ = ( ('CacheReads', wintypes.ULONG), ('CacheHits', wintypes.ULONG), ) class RESERVED_CACHE(ctypes.Structure): __slots__ = () _fields_ = ( ('ThinWireCache[', THINWIRECACHE * MAX_THINWIRECACHE), ) class CACHE_STATISTICS(ctypes.Structure): __slots__ = () class SPECIFIC(ctypes.Union): __slots__ = () _fields_ = ( ('ReservedCacheStats', RESERVED_CACHE), ('TShareCacheStats', wintypes.ULONG), ('Reserved', wintypes.ULONG * 20), ) _fields_ = ( ('ProtocolType', wintypes.USHORT), ('Length', wintypes.USHORT), ('Specific', SPECIFIC), ) class PROTOCOLSTATUS(ctypes.Structure): __slots__ = () _fields_ = ( ('Output', PROTOCOLCOUNTERS), ('Input', PROTOCOLCOUNTERS), ('Cache', CACHE_STATISTICS), ('AsyncSignal', wintypes.ULONG), ('AsyncSignalMask', wintypes.ULONG), ) class WINSTATIONINFORMATION(ctypes.Structure): __slots__ = () _fields_ = ( ('ConnectState', ctypes.c_long), ('WinStationName', wintypes.WCHAR * (WINSTATIONNAME_LENGTH + 1)), ('LogonId', wintypes.ULONG), ('ConnectTime', wintypes.LARGE_INTEGER), ('DisconnectTime', wintypes.LARGE_INTEGER), ('LastInputTime', wintypes.LARGE_INTEGER), ('LogonTime', wintypes.LARGE_INTEGER), ('Status', PROTOCOLSTATUS), ('Domain', wintypes.WCHAR * (DOMAIN_LENGTH + 1)), ('UserName', wintypes.WCHAR * (USERNAME_LENGTH + 1)), ('CurrentTime', wintypes.LARGE_INTEGER) ) winsta.WinStationQueryInformationW.restype = wintypes.BOOLEAN winsta.WinStationQueryInformationW.argtypes = ( wintypes.HANDLE, # ServerHandle wintypes.ULONG, # SessionId ctypes.c_long, # WinStationInformationClass wintypes.LPVOID, # pWinStationInformation wintypes.ULONG, # WinStationInformationLength wintypes.PULONG, # pReturnLength ) def get_idle_time(session_id=LOGONID_CURRENT, server_handle=SERVERNAME_CURRENT): info = WINSTATIONINFORMATION() rlen = wintypes.ULONG() if not winsta.WinStationQueryInformationW( server_handle, session_id,
Re: [python-win32] Unable to detect shutdown
On 3/9/22, H.Fujii wrote: > I'm currently developing a console program in Python 3.9. > I want to detect the shutdown of Windows and save the data to a file > safely. > I created a sample program by referring to the following URL. > https://docs.microsoft.com/en-us/windows/console/registering-a-control-handler-function > > However, the sample program does not detect shutdown > (CTRL_SHUTDOWN_EVENT) well. Console control events are sent by the Windows session server, csrss.exe, by creating a thread in the target process that starts at CtrlRoutine() in kernelbase.dll. This includes the cancel (i.e. Ctrl+C), break, and close events that are requested by a console session host (i.e. conhost.exe or openconsole.exe), as well as the system-initiated logoff and shutdown events. The session server sends the logoff and shutdown events to any process that isn't a GUI process. It doesn't matter whether or not the process is attached to a console. For example, the service control manager in services.exe depends on the console shutdown event, even though it is not a console application and is not attached to a console. A GUI process is one that's connected to a window station (e.g. interactive "WinSta0"), with one or more threads connected to a desktop (e.g. "WinSta0\Default"). Loading user32.dll, directly or indirectly, connects the process and loading thread to a window station and desktop (e.g. as set by its STARTUPINFO). In order to avoid getting converted to a GUI process, a console or background application has to carefully restrict itself to the base API, avoiding anything that might end up loading user32.dll, or by spawning a child process to call anything that loads user32.dll as a remote procedure call. Python 3.9+ starts out okay in this regard. But some extension modules in the standard library -- e.g. _ssl.pyd, _hashlib.pyd, and _ctypes.pyd -- do not delay-load dependencies that end up loading user32.dll. Thus, for example, it is currently impossible to use ctypes to handle the console logoff and shutdown events. As to win32api, it directly depends on user32.dll as well as indirectly via other DLLs. I think partitioning out a win32base module would be a worthy goal, but that's up to the PyWin32 project developers. If you can't take the simple route of using console logoff and shutdown events, then all you can do is fully embrace being a GUI process. Create a hidden top-level window with a message loop that handles WM_QUERYENDSESSION [1] and WM_ENDSESSION [2]. --- [1] https://docs.microsoft.com/en-us/windows/win32/shutdown/wm-queryendsession [2] https://docs.microsoft.com/en-us/windows/win32/shutdown/wm-endsession ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] Inheriting a command line - or how to efficiently replace Unix' os.execvpe
On 5/13/21, Sebastian M. Ernst wrote: > > ... but I am not managing to specify `parameters` so that I can actually > interact with `ssh`. Am I on the wrong track ... ? It's not clear to me what you need. Do you need to spawn and wait for an interactive ssh session that's driven normally by the user? Or do you need expect-like automation of an ssh session? ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] Inheriting a command line - or how to efficiently replace Unix' os.execvpe
On 5/12/21, Sebastian M. Ernst wrote: > > It starts the new process more or less like a child, but does not allow it to > control the command line. Instead, both the Python process and the child > die. The child merely manages to produce a few lines of output before that. First let's go over some basic information about consoles in Windows. Each console session [1] is implemented by a host process, which currently is either conhost.exe or openconsole.exe (used by Windows Terminal). Each Windows process can attach to one and only one console session at a time. A console session doesn't always have a window (i.e. the CREATE_NO_WINDOW process creation flag). Windows 10 also allows the console host to operate in pseudoconsole mode, which relays I/O to another application that implements the console/terminal user interface, or no UI at all. For example, each tab in Windows Terminal (wt.exe) is a pseudoconsole session that's hosted by an instance of openconsole.exe. A console session doesn't implement the POSIX concept of foreground and background process groups. Any process that's attached to a console can read and write from it. Generally when one starts a child process in the same console session, one either avoids using console I/O until the child exits, or one simply blocks and does nothing while waiting for the child to exit. Typically a command-line shell (e.g. cmd.exe) implements the latter. > `os.execvpe` is present in the Python standard library on Windows, but > it does not replace the original (Python) process. Windows is apparently > lacking relevant POSIX semantics. os.execve() is implemented internally as the equivalent of os.spawnve() with P_OVERLAY. I suggest you pretend that it does not exist. Windows does not support overlaying a new executable image on the current process, which is what callers reasonably expect from an exec() call. Instead, with P_OVERLAY the parent process spawns the child in a new process and then exits. If a grandparent process is waiting on the parent process, it will resume when the parent exits. The grandparent and its grandchild will then compete with each other for console I/O, which is an interleaved mess. You'll get a prompt from the grandparent, but the text you enter will be read by the grandchild. Then you'll get the grandchild's prompt, and so on. > `ssh` opens into a second, new `cmd.exe` window and can be interacted > with. The original `cmd.exe` window with Python in it remains open, > Python itself quits, returning control to `cmd.exe` itself. You're confusing things. cmd.exe is just a console client application. It has nothing to do with implementing a console session. If `ssh` is "ssh.bat", then it will execute via cmd.exe. But if it's an executable binary, either it will allocate a new console session if it doesn't inherit one (i.e. either the parent has no console, or the process was created with the CREATE_NEW_CONSOLE or CREATE_NO_WINDOW creation flag) or it will execute without a console if it's a detached process (i.e. the process was created with the DETACHED_PROCESS creation flag). You can pass these creation flags via the `creationflags` parameter of subprocess.Popen(). CREATE_NO_WINDOW is ignored if paired with either CREATE_NEW_CONSOLE or DETACHED_PROCESS, and the latter two obviously cannot be paired with each other. If you want to reuse the current console session, you'll have to spawn ssh, wait for it to exit, and proxy its exit status. That's the closest you can get to using exec*() in a POSIX system. --- [1] There's an unrelated concept of the active "Console" session in Windows terminal services. A Windows session contains a "WinSta0" interactive window station, which has input devices and a display and contains desktops such as the "Default" desktop, which contain windows. The "Console" session is the one that's connected to the physical console, i.e. the physical display, keyboard, and mouse. A Windows session also has the concept of a graphical shell, which is typically Windows Explorer. Generally speaking, however, the term "console" in Windows refers to a sub-session (i.e. subsystem) for text-based applications, which can exist in any Windows session, even in the non-interactive "Services" session. ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] Opening existing memory mapped files with pywin32
On 2/21/21, rhyslloyd1 wrote: > > In my head my ideal outcome is being able to map the whole memory mapped > file and not have to specify a size for it incase it changes size however I > don't understand the concept of a memory mapped file so I maybe my very > limited understanding is just incorrect, thanks in advance for any response! The address space of a process is managed in pages of a given fixed size (e.g. 4 KiB). The state of each page is tracked in an entry in the process page table, i.e. a page-table entry (PTE). When a page is sharable between processes, its PTE in a particular process references a prototype PTE that manages the overall state of the page (e.g. whether the page is resident in RAM or paged out to a filesystem paging file or data file). The system uses a kernel object called a Section to reference the prototype PTEs in a block of shared memory. In the Windows API, a Section is called a "file mapping" because the pages mapped into memory are always a byte range in a filesystem paging file or data file. At the time of creation via CreateFileMappingW, a file mapping can be assigned a name, either a global name for all sessions, or a local name in the current desktop session. This allows a process to easily try to open a file mapping (e.g. via OpenFileMapping) and use it to map all or a subset of the shared pages into its address space (e.g. via MapViewOfFile). In your case, I gather that you need to open a file mapping named "{8BA1E16C-FC54-4595-9782-E370A5FBE8DA}" and map the whole block of shared memory into the current process, without knowing how large it is. You'll need to query the size of the mapped region (i.e. the RegionSize) via VirtualQuery. Preferably also wrap the memory in an easy interface such as a memoryview, which should automatically unmap the memory when finalized. Here's a map_section(name, mode) function that implements this. Note that for reliable operation it requires memoryview.toreadonly(), which was added in Python 3.8. The underlying ctypes array allows writing to readonly memory, which will crash the process with an access violation. That's avoided in 3.8+ by returning a readonly memoryview when the shared memory is mapped without write access. import warnings import ctypes from ctypes import wintypes kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) FILE_MAP_READ = SECTION_MAP_READ = 0x0004 FILE_MAP_WRITE = SECTION_MAP_WRITE = 0x0002 class MEMORY_BASIC_INFORMATION(ctypes.Structure): _fields_ = (('BaseAddress', wintypes.LPVOID), ('AllocationBase', wintypes.LPVOID), ('AllocationProtect', wintypes.DWORD), ('PartitionId', wintypes.WORD), ('RegionSize', ctypes.c_size_t), ('State', wintypes.DWORD), ('Protect', wintypes.DWORD), ('Type', wintypes.DWORD)) PMEMORY_BASIC_INFORMATION = ctypes.POINTER(MEMORY_BASIC_INFORMATION) kernel32.OpenFileMappingW.restype = wintypes.HANDLE kernel32.OpenFileMappingW.argtypes = (wintypes.DWORD, wintypes.BOOL, wintypes.LPWSTR) kernel32.MapViewOfFile.restype = wintypes.LPVOID kernel32.MapViewOfFile.argtypes = (wintypes.HANDLE, wintypes.DWORD, wintypes.DWORD, wintypes.DWORD, ctypes.c_size_t) kernel32.UnmapViewOfFile.restype = wintypes.BOOL kernel32.UnmapViewOfFile.argtypes = (wintypes.LPCVOID,) kernel32.VirtualQuery.restype = ctypes.c_size_t kernel32.VirtualQuery.argtypes = (wintypes.LPCVOID, PMEMORY_BASIC_INFORMATION, ctypes.c_size_t) class BaseSharedMem(ctypes.Array): _type_ = ctypes.c_char _length_ = 0 def __del__(self, *, UnmapViewOfFile=kernel32.UnmapViewOfFile, warn=warnings.warn): if not UnmapViewOfFile(self): warn("UnmapViewOfFile failed", ResourceWarning, source=self) def map_section(name, mode='r'): mbi = MEMORY_BASIC_INFORMATION() if mode == 'r': access = FILE_MAP_READ elif mode == 'w': access = FILE_MAP_WRITE elif mode in ('rw', 'wr'): access = FILE_MAP_READ | FILE_MAP_WRITE else: raise ValueError('invalid mode') h = kernel32.OpenFileMappingW(access, False, name) if not h: raise ctypes.WinError(ctypes.get_last_error()) try: address = kernel32.MapViewOfFile(h, access, 0, 0, 0) if not address: raise ctypes.WinError(ctypes.get_last_error()) finally: kernel32.CloseHandle(h) result = kernel32.VirtualQuery(address, ctypes.byref(mbi), ctypes.sizeof(mbi)) if not result: ex = ctypes.WinError(ctypes.get_last_error()) if not kernel32.UnmapViewOfFile(address): ex2 = ctypes.WinError(ctypes.get_last_error()) raise ex2 from ex raise ex array_t = type('SharedMem_{}'.format(mbi.RegionSize), (BaseSharedMem,), {'_length_': mbi.RegionSize}) mv = memoryview(array_t.from_address(address)).cast('B') if 'w' not in mode: return
Re: [python-win32] Opening existing memory mapped files with pywin32
On 2/16/21, Tim Roberts wrote: > Eryk Sun wrote: >> >> I'm sorry to say that the only reliable solution is to directly call >> WinAPI OpenFileMappingW() and MapViewOfFile() using a foreign-function >> interface (FFI) package such as ctypes or CFFI. I can write up an >> example if you need help. > > Respectfully, I think you have misread the original message. He is a > relative beginner, just trying to do a simple file mapping. Based on his > description, mmap should be able to do what he needs. Sorry, I'm sure you're right, now that I think about it. I was responding reflexively based on the OP's code that attempts to create/open a file mapping with a name and 0 maximum size (i.e. unknown size; just map it all and query the size), since I've previously seen people trying and failing to do that with mmap in various IPC cases. The GUID name didn't help to clarify matters, since it's just as likely to be generated as a unique object name in the current session. I also think at unconscious level I didn't want to assume a confused misreading of the documented function parameters. Anyway, maybe the discussion will help someone else that's searching for information on the topic. ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] Opening existing memory mapped files with pywin32
On 2/16/21, rhyslloyd1 via python-win32 wrote: > > I am trying to open a memory mapped file using Python. I originally used the > "mmap" module from Python however I had issues with it because I had to use > a fixed size for the file even though my goal was to open an existing file > of which the size would be unknown. I'm sorry to say that the only reliable solution is to directly call WinAPI OpenFileMappingW() and MapViewOfFile() using a foreign-function interface (FFI) package such as ctypes or CFFI. I can write up an example if you need help. In theory, shared_memory.SharedMemory() in the multiprocessing package could support this case. But in practice it's still based on the mmap module. It tries to improve the situation by opening and mapping an existing mapping via WinAPI OpenFileMappingW() [1] and MapViewOfFile() [2] in order to query the region size via VirtualQuery(), but this isn't reliable because it returns the size rounded up to the nearest page boundary. Calling mmap.mmap(-1, size, tagname=name) with this queried size may fail the internal MapViewOfFile() call with ERROR_ACCESS_DENIED (5) if the size is bigger than the maximum size of the file mapping. (The underlying status from the NT system call more clearly describes the failure as STATUS_INVALID_VIEW_SIZE.) The problem with the standard library mmap module and PyWin32's mmapfile is that they're designed to open a mapping only via CreateFileMappingW() [3]. When this function is called with hFile as INVALID_HANDLE_VALUE (i.e. use the system paging file) and the maximum size as 0 (i.e. dwMaximumSizeHigh=0, dwMaximumSizeLow=0), it always fails with ERROR_INVALID_PARAMETER (87). It does not check whether lpName is the name of an existing mapping before validating the parameters and failing. Hypothetically, it could have been written to check for this case, since the file handle is ignored anyway for an existing mapping. But it is what it is, and has been so for about 30 years. A high-level library function needs to be aware of this limitation and handle it appropriately by implementing an open disposition (e.g. create-new, open-always, open-existing) that can dictate the use of OpenFileMappingW() in addition to or instead of CreateFileMapping(). --- [1] https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-openfilemappinga [2] https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-mapviewoffile [3] https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-createfilemappingw ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] DeviceIOControl using IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS fails with 'Incorrect function.'
On 2/9/21, Doug Campbell wrote: > > Again, you expand my knowledge! It seems so obvious now after reading what > you wrote that I would not be able to get volume disk extents for a physical > partition but yet this is what I wanted to do because I was attempting to > find out the partition's offset on the disk. In the case of a basic disk device, it can have multiple partitions that each correspond to a volume device, \Device\HarddiskVolume. In this case, each volume device consists of a single extent on a single disk. > The only way I can find to accomplish this is to iterate through each volume You can get the list of volume GUID names of volume devices that are registered with the mountpoint manager via FindFirstVolumeW / FindNextVolumeW. Unfortunately, PyWin32 doesn't wrap the volume-find functions. They can be called using ctypes. For example: import ctypes kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) class HANDLE(ctypes.c_void_p): def __eq__(self, other): if hasattr(other, 'value'): return self.value == other.value return False INVALID_HANDLE_VALUE = HANDLE(-1) ERROR_NO_MORE_FILES = 18 kernel32.FindFirstVolumeW.restype = HANDLE def list_volumes(): volumes = [] buf = (ctypes.c_wchar * 260)() hfind = kernel32.FindFirstVolumeW(buf, len(buf)) if hfind == INVALID_HANDLE_VALUE: raise ctypes.WinError(ctypes.get_last_error()) try: while True: # Strip the trailing backslash that FindNextVolumeW appends. # The trailing backslash is the mountpoint of the filesystem # that mounts the volume device, but we only want the GUID # device name. volumes.append(buf.value.rstrip('\\')) if not kernel32.FindNextVolumeW(hfind, buf, len(buf)): error = ctypes.get_last_error() if error != ERROR_NO_MORE_FILES: raise ctypes.WinError(error) break finally: kernel32.FindVolumeClose(hfind) return volumes I subclassed c_void_p to create the HANDLE type in order to avoid the otherwise automatic conversion of the return value to a builtin Python type. This is a simple way around needing to declare argtypes for the functions that take the find handle (actually a pointer to memory) as an argument. ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] DeviceIOControl using IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS fails with 'Incorrect function.'
On 2/9/21, Doug Campbell wrote: > > win32file.DeviceIoControl(hDevice, > winioctlcon.IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, > None, extents, None) > pywintypes.error: (1, 'DeviceIoControl', 'Incorrect function.') > > I have tried with all three of the disks on my system: \\.\PhysicalDrive0, > \\.\PhysicalDrive1, and \\.\PhyscialDrive2 but the results are always the > same. IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS requests the disk extents of a volume device. Disk devices do not implement this request. A volume device such as "\\.\C:" can span multiple extents across one or more physical disks such as "\\.\PhysicalDrive0" and "\\.\PhysicalDrive1". If the request fails with winerror.ERROR_MORE_DATA, you need to be prepared to resize the structure according to NumberOfDiskExtents. ctypes doesn't make this all that easy or convenient at a high level. What I do is to make the array field private and implement a property that takes the dynamic length into account. For example: ANYSIZE_ARRAY = 1 class VOLUME_DISK_EXTENTS(ctypes.Structure): _fields_ = (('NumberOfDiskExtents', ctypes.c_ulong), ('_Extents', DISK_EXTENT * ANYSIZE_ARRAY)) @property def Extents(self): offset = type(self)._Extents.offset array_t = DISK_EXTENT * self.NumberOfDiskExtents return array_t.from_buffer(self, offset) def resize(self): if self.NumberOfDiskExtents < 1: self.NumberOfDiskExtents = 1 offset = type(self)._Extents.offset array_size = ctypes.sizeof(DISK_EXTENT) * self.NumberOfDiskExtents ctypes.resize(self, offset + array_size) If you passed `vde = VOLUME_DISK_EXTENTS()`, and the request fails with winerror.ERROR_MORE_DATA, the call should have set NumberOfDiskExtents to the required length. Call vde.resize(), and try again. It's a bit clumsy, but it works. > hDevice = win32file.CreateFile( > disk, win32con.GENERIC_READ, > win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE, > None, win32con.OPEN_EXISTING, win32con.FILE_ATTRIBUTE_NORMAL, > None) IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS is defined to require FILE_ANY_ACCESS. This means the desired access and access sharing can both be 0, and should be since there's no reason to request access that you don't need that could cause the call to fail with a permission error (e.g. access denied, or a sharing violation if read access isn't shared). For example: volume = r'\\.\C:' hDevice = win32file.CreateFile(volume, 0, 0, None, win32file.OPEN_EXISTING, 0, None) --- Volume Device Names The base name of a volume device is typically something like "\Device\HarddiskVolume" or "\Device\CdRom", but these are automatic names that can vary in the device number N each time a volume comes online. The mountpoint manager usually will also assign persistent names to a volume device, which are stored in the registry if necessary (HKLM\System\MountedDevices) and are set globally in the "\GLOBAL??" device-alias directory when the volume comes online. Usually it assigns a GUID name of the form "Volume{12345678----123456789ABC}". A GUID name is used when mounting a volume on an existing directory (e.g. mounting a volume as "C:\Mount\BackupDrive"). It usually also assigns a DOS drive-letter name such as "C:", which provides the classic DOS volume mountpoint, such as "C:\". The "\GLOBAL??" object directory is rarely accessed directly, even in native NT API programs that have full access to the NT object namespace. Instead, native NT programs use "\??", a virtual directory that allows accessing global device symlinks in addition to, and shadowed by, device symlinks in the caller's logon session device directory. The SYSTEM logon uses "\GLOBAL??" for its logon session devices, so it always creates and accesses global device aliases. In the Windows API, the NT API "\??\" prefix maps to "\\.\" or "\\?\" UNC-style paths. The former is more common if just the device is accessed, and the latter is more common for a filesystem path. Thus to access the "C:" volume device directly, programs typically use "\\.\C:". Note that just "C:" by itself is a DOS drive-relative path that expands to the working directory in the mounted filesystem, which defaults to the root directory. So passing "C:" as the path to open is actually a reference to a filesystem directory, not to the volume device, which obviously won't work. ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] DeviceIOControl calls respond with parameter incorrect
On 2/9/21, Doug Campbell wrote: > > That was exactly what I needed. I will have to read up on the _pack_ > directive to understand it but for now things are running the way they > should be. I'm glad I could help. Normally padding is added between fields of a struct in order to support aligned access for the data type of each field. Setting the `_pack_ = 1` attribute forces single-byte alignment, and thus no padding bytes are added. > I did have one typo in what I originally pasted that made its way into what > you provided. > > ('volumeID', ctypes.c_wchar * VOLUME_ID_SIZE), > > should actually be > > ('volumeID', ctypes.c_char * VOLUME_ID_SIZE), Sorry, I did actually scan over the fields to check the types against the C definition, but I somehow missed that extra "w". ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] DeviceIOControl calls respond with parameter incorrect
On 2/8/21, Doug Campbell wrote: > In my python 2 script, I am trying to connect to the VeraCrypt device driver > to get some information on my mounted volumes. The VeraCrypt repo on GitHub [1] indicates that all structures are defined with #pragma pack(1). In ctypes this is the _pack_ directive. Try the following: import ctypes import winioctlcon import win32file def VC_IOCTL(CODE): return winioctlcon.CTL_CODE(winioctlcon.FILE_DEVICE_UNKNOWN, 0x800 + CODE, winioctlcon.METHOD_BUFFERED, winioctlcon.FILE_ANY_ACCESS) VC_IOCTL_GET_MOUNTED_VOLUMES = VC_IOCTL(6) VC_IOCTL_GET_VOLUME_PROPERTIES = VC_IOCTL(7) VC_IOCTL_GET_BOOT_ENCRYPTION_STATUS = VC_IOCTL(18) VC_IOCTL_GET_BOOT_DRIVE_VOLUME_PROPERTIES = VC_IOCTL(22) VC_IOCTL_EMERGENCY_CLEAR_KEYS = VC_IOCTL(41) MAX_PATH = 260 VOLUME_LABEL_LENGTH = 33 # 32 + null VOLUME_ID_SIZE = 32 WIN32_ROOT_PREFIX DRIVER_STR = r'\\.\VeraCrypt' class VOLUME_PROPERTIES_STRUCT(ctypes.Structure): _pack_ = 1 _fields_ = ( ('driveNo', ctypes.c_int), ('uniqueId', ctypes.c_int), ('wszVolume', ctypes.c_wchar * MAX_PATH), ('diskLength', ctypes.c_uint64), ('ea', ctypes.c_int), ('mode', ctypes.c_int), ('pkcs5', ctypes.c_int), ('pkcs5Iterations', ctypes.c_int), ('hiddenVolume', ctypes.c_int), ('readOnly', ctypes.c_int), ('removable', ctypes.c_int), ('partitionInInactiveSysEncScope', ctypes.c_int), ('volFormatVersion', ctypes.c_uint32), ('totalBytesRead', ctypes.c_uint64), ('totalBytesWritten', ctypes.c_uint64), ('hiddenVolProtection', ctypes.c_int), ('volFormatVersion', ctypes.c_int), ('volumePim', ctypes.c_int), ('wszLabel', ctypes.c_wchar * VOLUME_LABEL_LENGTH), ('bDriverSetLabel', ctypes.c_int), ('volumeID', ctypes.c_wchar * VOLUME_ID_SIZE), ('mountDisabled', ctypes.c_int)) prop = VOLUME_PROPERTIES_STRUCT(driveNo = ord('F') - ord('A')) hDevice = win32file.CreateFile(WIN32_ROOT_PREFIX DRIVER_STR, 0, 0, None, win32file.OPEN_EXISTING, 0, None) try: info = win32file.DeviceIoControl(hDevice, VC_IOCTL_GET_VOLUME_PROPERTIES, prop, prop) finally: hDevice.close() --- [1] https://github.com/veracrypt/VeraCrypt/blob/master/src/Common/Apidrvr.h ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] how to code pywin32 run existing window task scheduler?
On 1/14/21, Tim Roberts wrote: > On Jan 13, 2021, at 8:07 PM, Pongthorn Sangkaphet > >> I have tried already but error >> > That’s 0x80070005, which is ERROR_ACCESS_DENIED. Have you tried running > this from an elevated process? The default security descriptor of a task grants all access to administrators, and the principal user of the task is granted read access. Implicitly, however, the principal can execute a task as long the task security grants read access. Because the principal user can execute a task, this allows an administrator to configure a task to run elevated (i.e. with highest privileges) as the administrator user, which bypasses UAC (e.g. when executed via schtasks /run from a restricted security context). If the principal user has the batch logon right, then the task can also run in the services session (i.e. the option to run whether or not the user is logged on), but that's not pertinent to the question of execute access. If you need to allow some other user to read and execute a task (e.g. to bypass UAC in the context of the principal user), then you'll need to grant that user file read (FR) and execute (FX) access. Begin by retrieving the task's DACL_SECURITY_INFORMATION (4) in SDDL form from GetSecurityDescriptor [1]. You can convert this string to an SD record to work with it programmatically, or simply append an allow ACE in SDDL form, such as "(A;;FRFX;;;)". Then write the new DACL via SetSecurityDescriptor. --- [1] https://docs.microsoft.com/en-us/windows/win32/taskschd/registeredtask ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] win32process.CreateProcess: '%1 is not a valid Win32 application.'
On 12/9/20, Niko Pasanen wrote: > > So the problem might be that on the Person C's computer, there is actually > a file called "first" that is creating the error. The reason why I was > using win32process.CreateProcess like this was that I'm actually using > pywinauto, which uses win32process.CreateProcess. I'll have to still test > this, but apparently this will fix some possible bugs. Thank you! If you can't use lpApplicationName (preferred if the exact path is known) because you don't control the code, then just manually quote the executable path, e.g. pywinauto_call( f'"{exepath}"', ...). > The results for assoc and ftype are the same on my machine (should be tested > on person C's pc, too). The assoc and ftype commands have nothing to do with CreateProcess, so the suggestion to inspect them isn't particularly relevant. The only way the desktop shell's ".exe" file association might be relevant is if it's customized in some way to allow ShellExecuteEx to succeed even though CreateProcess on its own fails. The default association for ".exe" files defines a template command `"%1" %*`, which tells ShellExecuteEx to just call CreateProcess with the path of the executable and the command-line arguments. In theory the ".exe" association can be changed to proxy process creation with a launcher, but that would be a wonky thing to do, and it's not relevant to calling CreateProcess directly. FYI, assoc and ftype are old CMD shell builtin commands from NT 4.0 (1996) that get and set file associations and the "open" action of progids (f[ile]type program identifiers) in "HKLM\Software\Classes". These settings *might* be used by ShellExecuteEx. Nowadays these two commands are outdated and should not be relied on. "HKCU\Software\Classes" takes precedence, which these commands do not show. Numerous other registry keys for primary and fallback settings have also been added over the years, which these two commands also do not show. And, what's even more important is that the desktop shell was changed to cache and permanently store user choices separate from the default association selection algorithm. Cached choices and the pinned "UserChoice" are stored in "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\". ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] win32process.CreateProcess: '%1 is not a valid Win32 application.'
On 12/8/20, Niko Pasanen wrote: > > path_to_exe = Path(r"C:\path to some\folder\some.exe") FYI, the constructor of pathlib.Path supports forward slash as the path separator, which avoids the need to use a raw string. A WindowsPath instance uses backslash when converting back to a string. > (h_process, _, dw_process_id, _) = win32process.CreateProcess( > None, # module name > str(path_to_exe), # command line Since you just have an executable path, pass it in lpApplicationName and leave lpCommandLine as None. This is simpler than manually quoting the path. If the string in lpCommandLine is an unquoted executable path, Windows will consume spaces in it until it finds a non-directory match. For example, if it's r"C:\path to\some folder\some.exe", it will use r"C:\path to\some" or r"C:\path to\some.exe" if either exists and is not a directory. If the matching file isn't a valid PE image, it does not continue parsing the command line to consume the next space as "some folder". It simply fails the call with ERROR_BAD_EXE_FORMAT. > 1) The following DLLs on *both* machines with same path (mine & Person C) > - "python.exe" ERROR_BAD_EXE_FORMAT occurs when mapping the executable image into the process VM during the NtCreateUserProcess system call. At this stage, no dependent DLLs are mapped into the process. That occurs after the process has already been created, during process initialization. If it were a dependent DLL problem, then CreateProcess would succeed, but the child process would abnormally terminate with a status code such as STATUS_INVALID_IMAGE_FORMAT (0xC07B). ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] Help with PySECURITY_DESCRIPTOR
On 10/27/20, momc...@bojinov.info wrote: > > I m trying to store file's acl along with the backup of the file and then > restore it on the same system Consider using BackupRead() and BackupWrite() from the win32file module. These functions support backup and restore of data streams (default and alternate data streams), attributes, extended attributes, reparse data, object ID, and security. GENERIC_READ access includes the READ_CONTROL access that's required for reading most file security, but GENERIC_WRITE access isn't sufficient for restoring file security. Writing discretionary access-control entries and resource attributes requires WRITE_DAC access. Writing the owner, group, and mandatory label requires WRITE_OWNER access. Reading and writing audit entries and writing central-access-policy identifier entries requires ACCESS_SYSTEM_SECURITY access, which requires enabling SeSecurityPrivilege. Typically use GENERIC_WRITE | WRITE_DAC | WRITE_OWNER. In general you should backup and restore files using an elevated administrator account. Enable SeBackupPrivilege and SeRestorePrivilege in the process access token via OpenProcessToken, LookupPrivilegeValue, and AdjustTokenPrivileges, found in the win32security module. Open files with FILE_FLAG_BACKUP_SEMANTICS. This ensures access in most cases when the backup and restore privileges are enabled. The restore privilege also allows setting the file owner to the arbitrary owner of the source file instead being limited to the current user. Using an elevated logon (high integrity level) also allows restoring a high integrity level mandatory label on the destination file in case the source file has mandatory access control that denies write-up, read-up, or execute-up access. ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] Very unimportant bug report.
On 6/18/20, Vernon D. Cole wrote: > I am testing the installation of our newest version 228 of pywin32. > > When attempting to install pywidn32 on the 32-bit version of Python > 3.8 on *Windows 7 *32 bit, I get the following error: "The procedure > entry point ucrtbase.terminate could not be located in the dynamic > link library api-ms-win-crt-runtime-l1-1-0.dll." Python 3.8 officially supports Windows 7, but the OS has to be kept updated (e.g. KB2533623 is required for new loader flags and AddDllDirectory). I'd check optional Windows updates for a C runtime update. If that doesn't fix the problem, an issue should probably be opened -- at least for tracking. ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] pure python way to open a file with write deny for others
On 3/6/20, Robin Becker wrote: > > OK I want to read the (small) file completely. The other process may try to > re-write the file while I am reading it. I thought the other process already had the file open for writing, but apparently you just want to deny anyone the right to open the file with write access while you're reading it. If the file is already open with write access, you'll have to periodically retry opening it until you can get read access without sharing write access. Alternatively, you can use regular read-write sharing, but lock the file data via msvcrt.locking. This doesn't deny opening the file for write access, but any attempt to write to a locked region will fail with a locking violation. The locked region can encompass the entire file. (File locking in Windows doesn't prevent deleting or renaming a file, but the default share mode already prevents that.) > Of course the other process may adopt a completely orthogonal scheme of > opening with a different name and then renaming, Normally, open files in Python cannot be deleted or renamed until they're closed. This is the case as long as you have the file opened with just read or read-write sharing. Within the standard library, only the low-level open flag O_TEMPORARY uses shared delete/rename access. ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] pure python way to open a file with write deny for others
On 3/5/20, Robin Becker wrote: > I want to be able to read a windows file which is being periodically written > by another process. I'm having difficulty reconciling this sentence with the subject line. If you want to open a file for reading that's already open for writing, then the open has to share write access, not deny it (where to "deny" access means to not share access) [1]. That's not an issue since Python shares read and write access when opening a file. (Note that the share mode applies to all opens. It is not related to processes, so whether it's another process or the current process that's writing to the file is irrelevant to the problem.) I wouldn't use FILE streams in Python 3. I'd pass an `opener` to Python `open` that uses ctypes or PyWin32 to get a handle via `CreateFileW`, and then msvcrt.open_osfhandle to wrap the handle with a C file descriptor. The opener function signature is opener(filename, flags) -> fd. [1]: https://docs.microsoft.com/en-us/windows/win32/fileio/creating-and-opening-files ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] win32api.FindFiles hangs (was COM registration hangs up: 32 bit Python 3.7 on 64 bit Win 7)
On 12/19/18, Boylan, Ross wrote: > Eryk, thank you for that very detailed explanation. The directory is on a > network share; is NtQueryDirectoryFile[Ex] still the call that would be made > for it? It's the only system call to scan a directory. It's implemented in the I/O manager by sending an IRP_MJ_DIRECTORY_CONTROL: IRP_MN_QUERY_DIRECTORY request to the device stack. In this case the requested directory information is FileBothDirectoryInformation. Locally, the filesystem for a network share is the MUP device (multiple UNC provider), which proxies access to a redirector such as SMB (mrxsmb). Since the same call works from other systems, it's probably safe to assume the problem is in the local device stack and not the server on the other end of the wire. ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] win32api.FindFiles hangs (was COM registration hangs up: 32 bit Python 3.7 on 64 bit Win 7)
On 12/18/18, Boylan, Ross wrote: > BTW, I installed Visual Studio and wrote a little C++ program that exercised > FindNextFile. It failed in exactly the same way (returns same filename > forever if on a network drive and search spec has the exact file name), but > only when run from the original machine. The find-file handle returned by FindFirstFile[Ex] and used by FindNextFile is a pointer to a structure that includes the directory handle, a pointer to a 4 KiB file-record buffer (unless the caller requests FIND_FIRST_EX_LARGE_FETCH, which uses a 64 KiB buffer), and a pointer to the current entry's offset in the buffer. The buffer gets filled via the native system call NtQueryDirectoryFile[Ex]. The NextEntryOffset field of each variable-sized entry is used to traverse the buffer. The offset value is 0 in the last record. As the FindNextFile calls traverse the buffer, the current-entry pointer is updated for the next call. At the final entry, the pointer gets reset back to the base address. In this case, the next FindNextFile call will scan in a new batch of file records via NtQueryDirectoryFile[Ex]. This continues until the system call returns STATUS_NO_MORE_FILES (0x8006), in which case FindNextFile fails with the last error set to ERROR_NO_MORE_FILES (18). This is the normal way a directory scan is completed. Here's my guess about what's going wrong in your case. NtQueryDirectoryFile[Ex] can fail with STATUS_BUFFER_OVERFLOW (0x8005) if the buffer isn't large enough for a complete entry. In practice this shouldn't occur because the buffers used by FindFirstFile[Ex] and FindNextFile are large enough for at least one complete entry. We might have a filesystem that, on subsequent calls, stores a partial last entry in the buffer and fails with STATUS_BUFFER_OVERFLOW. FindNextFile handles this case by forgetting the partial entry, which it does by setting the NextEntryOffset field to 0 in the previous entry, if any. Then, even if there's only a single entry, it continues past the overflow error to return the current entry. When NtQueryDirectoryFile[Ex] is called again, it should resume at the entry that was partially returned in the previous call. But if it was only a single entry, and the buffer-overflow was due to a bug in the device stack, then we'll scan the same entry again with the same buffer-overflow error, and we're stuck in an infinite loop. Probably this bug is due to a filter driver in the device stack, rather than the filesystem driver. ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] Reg. taking folder ownership
On Tue, Aug 28, 2018 at 5:03 AM, Goku Balu wrote: >> Eryk wrote: >> This call will succeed even if one or more of the privileges wasn't >> modified. In this case GetLastError() returns ERROR_NOT_ALL_ASSIGNED >> (1300). This will be the case if you try to enable the take-ownership >> and restore privileges for a UAC restricted token. > > Thanks Eryk for responding. Yes it failed with 1300 and I'm running the code > from another admin account. But I think it's not a restricted account. I > could run any exe with Admin elevated privilege by right clicking and > choosing the option from context menu. Under UAC, administrators get logged on with two tokens. The one that LSA returns is a limited token with medium integrity level, from which administrative privileges have been stripped and for which the administrators group is enabled only for deny access checks. This limited token is paired with an elevated token, which has high integrity, full administrative privileges, and the administrators group enabled for both allow and deny access checks. The elevated token can only be obtained by a user with SeTcbPrivilege, such as the SYSTEM account. Creating an elevated process is commonly handled by the Application Information service, which runs as SYSTEM. It displays the consent dialog on the secure (Winlogon) desktop, gets the elevated token, and creates the process via CreateProcessAsUser. Run Python elevated, enable SeRestorePrivilege, and use GetNamedSecurityInfo and SetNamedSecurityInfo. This will allow modifying the owner or DACL regardless of the current DACL, even if no access is specifically granted to administrators. ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] Reg. taking folder ownership
On Mon, Aug 27, 2018 at 6:23 AM, Goku Balu wrote: > > My use case is this. Folder1 is created by Admin1 and ACL is set by Admin1. > Now Admin2 wants to change the ACL. I think we have two options here > 1) Take folder ownership and the do the changes > 2) Take elevated privileges for Admin2 account and add/remove ACL entries If the current DACL doesn't grant Admin2 the right to change the owner or change the DACL (i.e. to acquire said right), then you'll need to either enable SeTakeOwnershipPrivilege or enable SeRestorePrivilege with backup semantics. Under UAC restriction, an administrator only has these privileges when elevated, so option (2) is your only choice. SeRestorePrivilege also allows setting the owner to an arbitrary user or group. Otherwise you can only set the owner to either the current user or any of the user's groups that have the group-owner flag (e.g. the administrators group). > win32security.AdjustTokenPrivileges(handle, 0, new_privs) This call will succeed even if one or more of the privileges wasn't modified. In this case GetLastError() returns ERROR_NOT_ALL_ASSIGNED (1300). This will be the case if you try to enable the take-ownership and restore privileges for a UAC restricted token. > fs = win32security.GetFileSecurity( > path, win32security.OWNER_SECURITY_INFORMATION) > fs.SetSecurityDescriptorOwner(owner_sid, True) > > win32security.SetFileSecurity( > path, win32security.OWNER_SECURITY_INFORMATION, fs) Use GetNamedSecurityInfo and SetNamedSecurityInfo instead. These newer functions handle inheritance correctly. They also open the file with backup semantics. The I/O manager grants all requested modify access (including write-owner) if backup semantics is combined with SeRestorePrivilege. In this case you don't need SeTakeOwnershipPrivilege. ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] Where is MakeAbsoluteSD?
On Thu, Jun 28, 2018 at 4:49 AM, Rob Marshall wrote: > Hi, > > At the moment I don't need it at all, but when I use the smbcacls > utility from Samba on a Linux system and have it print the security > descriptor as an SDDL it gives me something like: > > O:S-1-5-21-3327876616-1579407131-3503203118-500G:S-1-5-21-3327876616- > 1579407131-3503203118-513D:P(A;;0x001e01ff;;;S-1-5-21-3327876616- > 1579407131-3503203118-500)(A;;0x00120089;;;S-1-5-21-3327876616- > 1579407131-3503203118-513)(A;;0x00120089;;;WD > > That's what I was hoping to get so that I could print it out. > Currently it doesn't matter because I can use smbcacls to get what I > want. There is no such thing as an absolute format SDDL string. Do you understand that "absolute" in this case is referring to absolute pointer addresses for the SD components *in memory*? This is opposed to the relative offset format that's used when marshaling the structure to disk or over the wire. The distinction is completely irrelevant to the SDDL string representation of an SD. The conversion from SDDL to SD always creates a self-relative format SD. It's also not relevant within the PyWin32 framework, since a PySECURITY_DESCRIPTOR is stored in self-relative format and automatically converts to absolute format whenever that's required. The boring details are handled for you automatically. ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] Where is MakeAbsoluteSD?
On Wed, Jun 27, 2018 at 3:14 PM, Rob Marshall wrote: > > I saw that, my problem is that I'm trying to use > win32security.ConvertSecurityDescriptorToStringSecurityDescriptor() > which is returning a self-relative SDDL and I need one that is > absolute. Is there a flag that I can set and get an absolute SDDL from > that function? In the Microsoft documentation for > ConvertSecurityDescriptorToStringSecurityDescriptor() it says, as to > what it "returns": Again, it doesn't matter what form the WinAPI function returns. Whatever it is, the PySECURITY_DESCRIPTOR object internally converts it to self-relative format. Why do you need an absolute format SD? Whatever the reason you'll need to use ctypes for part of the problem. ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] Where is MakeAbsoluteSD?
On Wed, Jun 27, 2018 at 5:04 AM, Rob Marshall wrote: > > I'm trying to convert a self-relative security descriptor, i.e. the > type that is returned by > win32security.ConvertStringSecurityDescriptorToSecurityDescriptor(), > to an absolute SD but I can't seem to find the MakeAbsoluteSD() > function. Where is it? A PySECURITY_DESCRIPTOR object is stored in self-relative format. Internally it's converted to absolute format for functions that require it, such as SetSecurityDescriptorDacl. See the source: https://github.com/mhammond/pywin32/blob/b223/win32/src/PySECURITY_DESCRIPTOR.cpp ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] pywintypes.error: (5, 'RegSetValueEx', 'Access denied')
On Thu, Mar 15, 2018 at 8:44 AM, Robin Kluthwrote: > > I use Python 2.7 on win10 and pywin32 223. I want to log messqages to the > EventLog: > > dllname = os.path.dirname(__file__) > ntl = NTEventLogHandler("Python Logging Test", dllname=dllname) The Python module should not be used for dllname. This needs to be a PE image (e.g. PYD, DLL, EXE) that has an embedded message table. NTEventLogHandler currently just hard codes using event ID 1, which is "%1\r\n" in win32evtlog.pyd or win32service.pyd. The "%1" insert gets replaced by the message text when the log record is formatted. In your case, just leave it as the default. > Traceback (most recent call last): > File "fpp_user_import.py", line 44, in > ntl = NTEventLogHandler("Python Logging Test", dllname=dllname) > File "C:\Python27\lib\logging\handlers.py", line 987, in __init__ > self._welu.AddSourceToRegistry(appname, dllname, logtype) > File "C:\Python27\lib\site-packages\win32\lib\win32evtlogutil.py", line > 42, in AddSourceToRegistry > msgDLL) > pywintypes.error: (5, 'RegSetValueEx', 'Zugriff verweigert') > > If I run the script with Admin privs, it is working. > > How to log as "normal" user? Am I missing something? This should be enhanced in NTEventLogHandler. It's not the end of the world if this key doesn't get created. The event viewer will just show a warning that the event ID can't be found for the given source. Also, when installed an application can request elevation to register the event log source. Given this, I suggest you work around the problem with a subclass that ignores this error. For example: import logging import logging.handlers class NTEventLogHandler(logging.handlers.NTEventLogHandler): __doc__ = logging.handlers.NTEventLogHandler.__doc__ def __init__(self, appname, dllname=None, logtype="Application"): logging.Handler.__init__(self) self.appname = appname self.dllname = dllname self.logtype = logtype try: import win32evtlogutil, win32evtlog, winerror except ImportError: print('The Python Win32 extensions for NT (service, event ' 'logging) appear not to be available.') self._welu = None return self._welu = win32evtlogutil self.deftype = win32evtlog.EVENTLOG_ERROR_TYPE self.typemap = { logging.DEBUG : win32evtlog.EVENTLOG_INFORMATION_TYPE, logging.INFO: win32evtlog.EVENTLOG_INFORMATION_TYPE, logging.WARNING : win32evtlog.EVENTLOG_WARNING_TYPE, logging.ERROR : win32evtlog.EVENTLOG_ERROR_TYPE, logging.CRITICAL: win32evtlog.EVENTLOG_ERROR_TYPE, } try: win32evtlogutil.AddSourceToRegistry(appname, dllname, logtype) except win32evtlogutil.error as e: if e.winerror != winerror.ERROR_ACCESS_DENIED: raise ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] playing with scintillacon
On Fri, Dec 15, 2017 at 9:10 PM, Kurt Eilanderwrote: > Ok, I found scintilla.dll in the directory above scintillacon.py, but it > came with pywin32, so it *should* be the correct one. > DLL inspector says it's a 64-bit, which is correct for my os. > > I copy that dll, and indeed, all binaries in that directory (just to be > sure) into my program directory and it still does not work! Try importing win32api in the same context. If that fails, check PATH for pywintypesXY.dll: where pywintypes*.dll > I have no idea what could be wrong. Oh! To know what that little %1 means! System error message include inserts such as %1 for when errors and exceptions are shown by the system itself. All Python has in this case is the error code, ERROR_BAD_EXE_FORMAT. So when it calls FormatMessage, it uses the flag FORMAT_MESSAGE_IGNORE_INSERTS, as it should. ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] os.remove not deleting a file
On Tue, Oct 31, 2017 at 1:16 PM, Henk Zevenhuizenwrote: > > Then i get the message: "after", the file is still there and i cannot delete > the through windows explorer (permission denied) > the only way to delete the file is killing python Does Explorer explicitly tell you "permission denied" or "file access denied", or is it a "sharing violation" or "file in use" error? The common case is a sharing violation (error 32). Deleting a file requires opening it with delete (DELETE) access. Whether or not that's allowed depends in part on how the file is already open. Filesystems maintain counts of the number of times a file is open; the number of opens that have read (or execute), write (or append), or delete access; and the number that share read (or execute), write (or append), or delete access. In order for an open with delete access to not fail with a sharing violation, all previous opens must share delete access. Also, if any previous open has delete access, then the current open has to share delete access. IMO, access sharing should not be called a 'lock'. It could be confused with Windows file locking, which is a completely different system. File locking does not prevent file deletion. For example, Python defaults to opening files with read and write sharing, but not delete sharing: >>> fd = os.open('test.txt', os.O_CREAT) >>> os.remove('test.txt') Traceback (most recent call last): File "", line 1, in WindowsError: [Error 32] The process cannot access the file because it is being used by another process: 'test.txt' Once the file handle is closed, os.remove (WinAPI DeleteFile) succeeds: >>> os.close(fd) >>> os.remove('test.txt') >>> os.listdir('.') [] If, on the other hand, Explorer is subsequently being denied delete access (error 5), then maybe your Python program actually has the file open with delete sharing. In this case, initially 'deleting' the file will succeed, but it won't be unlinked, and re-opening the file will fail with access denied. For example, the O_TEMPORARY flag opens a file with delete sharing: >>> fd = os.open('test.txt', os.O_CREAT | os.O_TEMPORARY) >>> os.remove('test.txt') DeleteFile succeeds this time, but in Windows that only means the file's delete disposition is set. This is a flag on the underlying file/stream control block (FCB or SCB) that tells the filesystem to unlink the file when all existing references to it have been closed. We still have a handle open, so the file is not unlinked, as you can see via listdir: >>> os.listdir('.') ['test.txt'] When the delete disposition is set, it's impossible to re-open the file for any access, even the most basic access to stat() file metadata: >>> os.stat('test.txt') Traceback (most recent call last): File "", line 1, in WindowsError: [Error 5] Access is denied: 'test.txt' The file is unlinked after the handle is closed: >>> os.close(fd) >>> os.listdir('.') [] That would have happened even without calling os.remove because O_TEMPORARY flags the opened file as delete-on-close. ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] CreateDesktop() and displaying a window on it
On Fri, Apr 7, 2017 at 4:35 PM, Tim Robertswrote: > I see Eryk provided the explanation, but all I had to do to make your > example work was add > hDesk.SetThreadDesktop() > just after your SwitchDesktop call. That didn't work for me before. It was causing my Windows 10 system to get stuck on a blank desktop. The SetThreadDesktop call was failing, which I assumed was because tkinter was automatically creating an invisible window or hook in the main thread. That's why I used a new thread as a workaround. But just now it worked. Go figure. ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] CreateDesktop() and displaying a window on it
On Thu, Apr 6, 2017 at 7:38 PM, Kurt Eilanderwrote: > Hey all, > > I'm trying to get system modal functionality like the UAC dialog. > > According to http://developex.com/blog/system-modal-back/ the thing to do is > to create and switch to a new desktop. > > Therefore, I'm doing: > > hDeskOld=win32service.GetThreadDesktop(win32api.GetCurrentThreadId()) SwitchDesktop affects which desktop has input focus. SetThreadDesktop affects which desktop is used by the current thread. You need to open a handle to the current input desktop to switch back to it at the end. I'm far from a Tk expert. I don't know how to close out the resources it uses to be able to call SetThreadDesktop. I'm not going to bother figuring this out to get this working on the main thread. I'll just create a new thread; pass it the handle for the new desktop; save the existing input desktop; set the thread desktop; and switch the input desktop. Then on teardown switch back to the original input desktop and close the desktop handles. Since I can't reliably call SetThreadDesktop to restore the worker thread's desktop before the thread exits, there's some asynchronous overlap when the thread and its resources haven't been destroyed yet. To address the potential for getting ERROR_BUSY when calling CloseDesktop from the main thread, I'm adding a retry loop that defaults to 10 attempts, with a delay of 0.1s. It shouldn't need more than 2 attempts. import sys import time import tkinter import threading import winerror import win32con import win32api import win32service def close_desktop(hDesk, numtries=10): for i in range(numtries): try: return hDesk.CloseDesktop() except win32service.error as e: if e.winerror != winerror.ERROR_BUSY or i == numtries - 1: raise time.sleep(0.1) def dialog(hDesk, title='SysModalDialog', message='', img=None): hDeskInputOld = win32service.OpenInputDesktop(0, False, win32con.DESKTOP_SWITCHDESKTOP) try: hDesk.SetThreadDesktop() hDesk.SwitchDesktop() try: root = tkinter.Tk() #root.mainloop() app = SysmodalDialog(root, title, message, img) app.mainloop() finally: hDeskInputOld.SwitchDesktop() finally: close_desktop(hDeskInputOld) def main(): hDesk = win32service.CreateDesktop("SysModalDesktop", 0, win32con.GENERIC_ALL, None) try: t = threading.Thread(target=dialog, args=(hDesk,)) t.start() t.join() finally: close_desktop(hDesk) return 0 if __name__ == '__main__': sys.exit(main()) ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] win32api handle incompatible with ctypes?
On Sun, Mar 5, 2017 at 12:16 AM, Kurt Eilanderwrote: > > I'm having another problem. I'm wanting to get the size of a dll resource, > but... > > When I do: > try: > hLib=win32api.GetModuleHandle(fileName) > except: > hLib=win32api.LoadLibrary(fileName) > if hLib==None: > raise WindowsError('File not found, '+fileName) > hResInfo=ctypes.windll.kernel32.FindResourceW(hLib,index,type) > size=ctypes.windll.kernel32.SizeofResource(hLib,hResInfo) > > It throws: > hResInfo=ctypes.windll.kernel32.FindResourceW(hLib,index,type) > ctypes.ArgumentError: argument 1: : > long int too long to convert > > Almost like ctypes doesn't like the win32api handle. > > My machine is 64 bit. Is that what ctypes is not liking? Is there a way > around it? The default conversion for integer arguments is to a 32-bit C int. The implementation calls PyLong_AsLong, for which a 64-bit address can raise an overflow exception on Windows since a C long is always 32-bit on this platform. You're lucky to get an exception. The problem is worse on Unix systems that have a 64-bit C long. The conversion doesn't overflow, but ctypes itself silently casts the address to a 32-bit int. Invariably this causes a segfault, either directly or indirectly due to stack or heap corruption. It looks like you're also not setting restype to a pointer type for FindResourceW. Like with parameters, the default integer conversion type for a function result is a 32-bit C int. A 64-bit result will be silently truncated on Windows as well. Kernel and User handles never exceed 32-bit values, but don't assume that's true of all handles. HMODULE handles break this rule, as do others. Always assume a handle type requires the full range of a pointer. Here are some general suggestions. Use ctypes.WinDLL instead of ctypes.windll (the latter was a bad design for multiple reasons). Load the library with the option use_last_error=True, unless you're working with NT or COM libraries that return NTSTATUS or HRESULT values. Always set the function prototype -- at least argtypes; restype if the function returns a pointer type; and preferably an errcheck function that raises idiomatic exceptions. For example: import winerror import win32api import ctypes from ctypes import wintypes kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) def _check_zero(result, func, args): if not result: raise ctypes.WinError(ctypes.get_last_error()) return args kernel32.FindResourceW.errcheck = _check_zero kernel32.FindResourceW.restype = wintypes.HRSRC kernel32.FindResourceW.argtypes = ( wintypes.HMODULE, # _In_opt_ hModule wintypes.LPCWSTR, # _In_ lpName wintypes.LPCWSTR) # _In_ lpType kernel32.LoadResource.errcheck = _check_zero kernel32.LoadResource.restype = wintypes.HGLOBAL kernel32.LoadResource.argtypes = ( wintypes.HMODULE, # _In_opt_ hModule wintypes.HRSRC) # _In_ hResInfo kernel32.SizeofResource.errcheck = _check_zero kernel32.SizeofResource.restype = wintypes.DWORD kernel32.SizeofResource.argtypes = ( wintypes.HMODULE, # _In_opt_ hModule wintypes.HRSRC) # _In_ hResInfo kernel32.LockResource.restype = wintypes.LPVOID kernel32.LockResource.argtypes = ( wintypes.HGLOBAL,) # _In_ hResData def get_resource(filename, index, rtype): try: hLib = win32api.GetModuleHandle(filename) except win32api.error as e: if e.winerror != winerror.ERROR_MOD_NOT_FOUND: raise hLib = win32api.LoadLibrary(filename) index = wintypes.LPCWSTR(index) # MAKEINTRESOURCE rtype = wintypes.LPCWSTR(rtype) hResInfo = kernel32.FindResourceW(hLib, index, rtype) hRes = kernel32.LoadResource(hLib, hResInfo) size = kernel32.SizeofResource(hLib, hResInfo) addr = kernel32.LockResource(hRes) return ctypes.string_at(addr, size) if __name__ == '__main__': RT_MANIFEST = 24 manifest = get_resource(None, 1, RT_MANIFEST).decode('utf-8') print(manifest) The above demo prints the embedded manifest from python.exe. Of course, it would be a lot simpler to use win32api.LoadResource [1]: manifest = win32api.LoadResource(None, RT_MANIFEST, 1).decode('utf-8') [1]: http://docs.activestate.com/activepython/3.4/pywin32/win32api__LoadResource_meth.html ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] How can I restrict users from changing ACE?
On Thu, Oct 6, 2016 at 11:36 AM, Francoi Xavierwrote: > I've just started learning about windows ACL and file permissions. The task > here is to make a file/folder read-only and should get deleted/modified only > through our client software. I've denied Write, Delete and other permissions > for the Lookup Name Everyone which generally restricted users from changing > the files/folders. Don't forget to also deny FILE_DELETE_CHILD access on the directory. Otherwise a user with that right can delete files even when the DACL otherwise denies delete access. > But the current user who also happens to be the creator/owner of the > file/folder can delete the ACE which has been added and he can gain complete > access rights. Is there a way to restrict this behaviour so that users > cannot change the file access permissions? In Vista and later (NT 6+) you can deny WRITE_DAC access to the "OWNER RIGHTS" security principal. The owner of the object won't be able to modify the permissions. ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] python-win32 Digest, Vol 162, Issue 4
On Fri, Sep 23, 2016 at 2:12 PM, eryk sun <eryk...@gmail.com> wrote: > Each kernel object type has a GENERIC_MAPPING that maps generic rights > to sets of standard and object-specific rights. Before doing an > AccessCheck, generic rights have to be mapped to specific rights via > MapGenericMask. > > For the File type this generic mapping consists of the following values: > > FILE_GENERIC_READ > FILE_GENERIC_WRITE > FILE_GENERIC_EXECUTE > FILE_ALL_ACCESS > > If you deny GENERIC_WRITE for a File, that's the same as denying the 6 > rights in FILE_GENERIC_WRITE, which includes the standard SYNCHRONIZE > and READ_CONTROL rights. You need to mask the value to filter out > rights that shouldn't be denied. Use the constant SPECIFIC_RIGHTS_ALL, > which is defined as 0x (i.e. the lower 16 bits of an access mask > are reserved for object-specific rights). For example: For reference, here's an access mask diagram: 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +---+---+---+ |G|G|G|G|Resvd|A| StandardRights| SpecificRights| |R|W|E|A| |S| | | +-+-+---+---+ Generic Read Generic Write Generic Execute Generic All Reserved: 3 Access SACL Standard Rights: 8 Specific Rights: 16 The four most significant bits are the generic rights. Before evaluating an AccessCheck, the system maps generic rights in access masks to the corresponding standard and specific rights. Only 5 of the 8 possible standard rights have been assigned: SYNCHRONIZE (bit 20), WRITE_OWNER, WRITE_DAC, READ_CONTROL, and DELETE (bit 16). The File type assigns 9 out of 16 possible specific rights, from FILE_WRITE_ATTRIBUTES (bit 8) down to FILE_READ_DATA (bit 0). Some bits have multiple meanings depending on whether the object is a directory, data file, or named pipe. For example, bit 2 can mean FILE_ADD_SUBDIRECTORY, FILE_APPEND_DATA, or FILE_CREATE_PIPE_INSTANCE. ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] python-win32 Digest, Vol 162, Issue 4
On Fri, Sep 23, 2016 at 10:39 AM, Goku Baluwrote: > On Tue, Sep 20, 2016 at 9:30 PM, wrote: >> > >> > That doesn't seem like a bug to me. GENERIC_WRITE represents several >> > permissions mashed together, including FILE_WRITE and read control. >> > >> > Perhaps try with just FILE_WRITE on its own? >> >> For a file or directory, GENERIC_WRITE (0x8000) gets mapped to >> FILE_GENERIC_WRITE, which includes the following standard rights >> (upper 16 bits): >> >> SYNCHRONIZE = 0x0010 >> READ_CONTROL = 0x0002 >> >> and File-object specific rights (lower 16 bits): >> >> FILE_WRITE_ATTRIBUTES = 0x0100 >> FILE_WRITE_EA = 0x0010 >> FILE_APPEND_DATA = 0x0004 >> FILE_WRITE_DATA = 0x0002 >> >> The relevant access right that's being denied in this case is SYNCHRONIZE. > > So if we deny WRITE then SYNCHRONIZE will be denied which will in-turn > affect READ. Is there a way to deny WRITE alone without affecting > file/folder read? Each kernel object type has a GENERIC_MAPPING that maps generic rights to sets of standard and object-specific rights. Before doing an AccessCheck, generic rights have to be mapped to specific rights via MapGenericMask. For the File type this generic mapping consists of the following values: FILE_GENERIC_READ FILE_GENERIC_WRITE FILE_GENERIC_EXECUTE FILE_ALL_ACCESS If you deny GENERIC_WRITE for a File, that's the same as denying the 6 rights in FILE_GENERIC_WRITE, which includes the standard SYNCHRONIZE and READ_CONTROL rights. You need to mask the value to filter out rights that shouldn't be denied. Use the constant SPECIFIC_RIGHTS_ALL, which is defined as 0x (i.e. the lower 16 bits of an access mask are reserved for object-specific rights). For example: import win32security import ntsecuritycon WORLD = win32security.CreateWellKnownSid(win32security.WinWorldSid) FILE_WRITE = (ntsecuritycon.FILE_GENERIC_WRITE & ntsecuritycon.SPECIFIC_RIGHTS_ALL) def deny_write(filename, account=None, ace_flags=0): sd = win32security.GetFileSecurity( filename, win32security.DACL_SECURITY_INFORMATION) sid = WORLD if account is None else ( win32security.LookupAccountName(None, account)[0]) dacl = sd.GetSecurityDescriptorDacl() dacl.AddAccessDeniedAceEx( win32security.ACL_REVISION_DS, ace_flags, FILE_WRITE, sid) sd.SetSecurityDescriptorDacl(1, dacl, 0) win32security.SetFileSecurity( filename, win32security.DACL_SECURITY_INFORMATION, sd) For a directory, generic write access entails the ability to write attributes and add files and subdirectories. Note that file delete rights are separately controlled by standard DELETE access and the specific directory right FILE_DELETE_CHILD, which is the right to delete files or subdirectories of a directory, even if the user is otherwise denied or not granted DELETE access. The ace_flags parameter allows controlling whether the ACE is inherited by subdirectories and files (CONTAINER_INHERIT_ACE, OBJECT_INHERIT_ACE) , whether the inheritance flags get propagated (NO_PROPAGATE_INHERIT_ACE), and whether the ACE applies only for inheritance (INHERIT_ONLY_ACE). ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] File access entries are incorrectly set
On Mon, Sep 19, 2016 at 10:21 PM, Christopher Nilssonwrote: > > That doesn't seem like a bug to me. GENERIC_WRITE represents several > permissions mashed together, including FILE_WRITE and read control. > > Perhaps try with just FILE_WRITE on its own? For a file or directory, GENERIC_WRITE (0x8000) gets mapped to FILE_GENERIC_WRITE, which includes the following standard rights (upper 16 bits): SYNCHRONIZE = 0x0010 READ_CONTROL = 0x0002 and File-object specific rights (lower 16 bits): FILE_WRITE_ATTRIBUTES = 0x0100 FILE_WRITE_EA = 0x0010 FILE_APPEND_DATA = 0x0004 FILE_WRITE_DATA = 0x0002 The relevant access right that's being denied in this case is SYNCHRONIZE. For example, SetCurrentDirectory uses a synchronous directory handle, i.e. it calls NtOpenfile with desired access SYNCHRONIZE | FILE_TRAVERSE and open option FILE_SYNCHRONOUS_IO_NONALERT. Similarly, for listing a directory, FindFirstFile uses a synchronous handle with SYNCHRONIZE | FILE_LIST_DIRECTORY access. The open option FILE_SYNCHRONOUS_IO_NONALERT is defined as follows: All operations on the file are performed synchronously. Waits in the system that synchronize I/O queuing and completion are not subject to alerts. This flag also causes the I/O system to maintain the file-position context. If this flag is set, the SYNCHRONIZE flag must be set in the DesiredAccess parameter. For example, when FindFirstFile calls NtQueryDirectoryFile (FileBothDirectoryInformation) to list the directory, the system call sees the handle is opened for synchronous access, so it waits to acquire the File object's Lock before calling the filesystem driver (e.g. NTFS). Then it waits again to complete the request. ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] win32com.server failure: Is debugging possible?
On Mon, Aug 29, 2016 at 12:08 AM, Bob Hoodwrote: > Breaking on that function produced the following stack trace: I'm glad you found the culprit, but actually the "ModLoad" lines are just printed by the debugger as DLLs are loaded. The stac[k] trace [1] attempts to walk the stack frames back based on the current stack and instruction pointers (e.g. rsp and rip for an x64 ISA). The default is up to 20 frames if a frame count isn't specified. Another starting point when diagnosing a crash is `!analyze -v` [2] to get a verbose summary of the exception, but that doesn't apply in your case. [1]: https://msdn.microsoft.com/en-us/library/ff551943 [2]: https://msdn.microsoft.com/en-us/library/ff562112 ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] win32com.server failure: Is debugging possible?
On Sat, Aug 27, 2016 at 2:19 PM, Bob Hoodwrote: > > From what I can tell, it's not actually a crash. It appears to be an exit() > with a result of 1, so it's not going to be easy to track down. Break on ntdll!RtlExitUserProcess to print a stack trace. ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] win32com.server failure: Is debugging possible?
On Fri, Aug 26, 2016 at 12:42 AM, Bob Hoodwrote: > Any suggestions as to how I could determine the cause of the crash without > having to uninstall ALL of my software? Configure a postmortem debugger [1] (e.g. windbg -I). Use gflags to temporarily enable full page-heap verification for Explorer. [1]: https://msdn.microsoft.com/en-us/library/ff551063 ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] Problem registering COM server at installation time
On Sun, Jan 31, 2016 at 10:42 PM, Mark Hammondwrote: > On 17/01/2016 6:51 AM, Malte Forkel wrote: >> >> I'm trying the register a COM server using the install script of a built >> distribution, created with the bdist_wininst format of distutils. But >> the script fails with a message that MSVCR90.dll can't be found. >> Registering the server after the installer is done works fine. I'm using >> Python 2.7.11 and pywin32-220 on Windows 7.1 64 bit. > > That sounds a little strange - the script should only be executed after > python27.dll is loaded, which itself relies on msvcr90.dll. > > Does it happen to work if you put the installer executable in the same > directory as python27.dll? Loading msvcr90.dll requires a SxS activation context. Neither pythoncom27.dll nor pywintypes27.dll have this assembly embedded as resource #2. I assume that's because they're normally loaded as extension modules. In this case Python uses the activation context that gets saved in its DllMain, which includes the VC90 assembly from the #2 manifest that's embedded in python27.dll. Are these two DLLs getting loaded in some other way here? If so you can create a context at runtime from the manifest that's embedded in python27.dll. pythoncomloader27.dll can load msvcr90.dll because it has the following manifest embedded as resource #2: ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
[python-win32] python-win32 is for Python on Windows
On Sat, Dec 26, 2015 at 10:44 AM, Vernon D. Colewrote: > > 1) this (Python-win32) is not a group for the discussion of Python (in > general) on Windows platforms. It is for discussion of a specific toolkit > (pywin32) Actually, that's not right: python-win32 -- Python on Windows (32-bit and 64-bit) About python-win32: All issues related to programming Python on Windows. 32-bit and 64-bit, pywin32 extensions, COM, you name it. There was some discussion a bit ago about changing the list name to "python-windows", because "win32" is a legacy name. (Microsoft changed the API name to "Windows" a while ago.) The discussion resulted in changing the list title from "Python on win32" [1] to "Python on Windows (32-bit and 64-bit)". [1]: https://web.archive.org/web/20150905161144/https://mail.python.org/mailman/listinfo/python-win32 ___ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32
Re: [python-win32] drag files with non-ASCII filenames?
On Tue, Dec 15, 2015 at 2:27 PM, Tim Robertswrote: > > The Windows console shell is an 8-bit entity. That means you only have > 256 characters available at any given time, similar to they way > non-Unicode strings work in Python 2. The input and screen buffers of the console (conhost.exe) are UCS-2, which has been the case since NT 3.1 was released in 1993. There are display limits, such as not being able to mix narrow and wide glyphs and not handling characters composed with multiple codes (such as UTF-16 surrogate pairs). Regardless of what's displayed, the wide-character API preserves the underlying UTF-16 text. That said, handling the input buffer requires special care due to how it represents characters that aren't mapped by the current keyboard layout. In this case, the WindowProc of conhost.exe handles a WM_DROPFILES [1] message as if it's pasted from the clipboard. It loops over the string to create an INPUT_RECORD [2] array. Each character is mapped in the current keyboard layout via VkKeyScan [3]. If this fails, the console uses a sequence of Alt+Numpad key event records. (At the end of this reply I'm including a commented transcript of a session with a debugger attached to conhost.exe in Windows 10. I set a breakpoint on s_DoStringPaste to watch how it handled pasting "À" into the input buffer.) A client program that calls ReadConsoleW [4] doesn't have to worry about this. The console internally handles decoding the Alt+Numpad sequence when it writes the input to the caller's wide-character buffer. Microsoft's getwch function instead calls ReadConsoleInputW [5] to be able to read extended keys and avoid discarding non-keyboard events, but it doesn't handle the Alt+Numpad case. Handling these sequences requires a custom implementation of kbhit and getwch. An example that gets this right is the PDCurses [6] library, when compiled using the wide-character API. Christoph Gohlke has a Python curses module [7] for Windows that uses PDCurses, but only the Python 3 version is compiled with Unicode support. If extended key support (e.g. arrow and function keys) and preserving mouse, window buffer, and focus events doesn't matter, then just disable the console's line input and echo mode, and call ReadConsoleW to read a character at a time. This lets the console handle the Alt+Numpad events for you. Here's example ctypes code for this limited implementation of kbhit and getwch. It's not broadly tested, so caveat emptor. I did check that it worked with file drops and pasting Unicode strings into the console, as well as manual Alt+Numpad input. import msvcrt import ctypes from ctypes import wintypes kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) STD_INPUT_HANDLE = -10 KEY_EVENT = 1 VK_MENU = 0x12 ENABLE_LINE_INPUT = 2 ENABLE_ECHO_INPUT = 4 wintypes.CHAR = ctypes.c_char class INPUT_RECORD(ctypes.Structure): class EVENT_RECORD(ctypes.Union): class KEY_EVENT_RECORD(ctypes.Structure): class UCHAR(ctypes.Union): _fields_ = (('UnicodeChar', wintypes.WCHAR), ('AsciiChar', wintypes.CHAR)) _fields_ = (('bKeyDown', wintypes.BOOL), ('wRepeatCount', wintypes.WORD), ('wVirtualKeyCode', wintypes.WORD), ('wVirtualScanCode', wintypes.WORD), ('uChar', UCHAR), ('dwControlKeyState', wintypes.DWORD)) _fields_ = (('KeyEvent', KEY_EVENT_RECORD),) _fields_ = (('EventType', wintypes.WORD), ('Event', EVENT_RECORD)) def kbhit(): handle = kernel32.GetStdHandle(STD_INPUT_HANDLE) npend = wintypes.DWORD() npeek = wintypes.DWORD() if (not kernel32.GetNumberOfConsoleInputEvents( handle, ctypes.byref(npend)) or npend.value == 0): return False inbuf = (INPUT_RECORD * npend.value)() if (not kernel32.PeekConsoleInputW( handle, inbuf, npend, ctypes.byref(npeek)) or npeek.value == 0): return False peek = (INPUT_RECORD * npeek.value).from_buffer(inbuf) for p in peek: if p.EventType != KEY_EVENT: continue e = p.Event.KeyEvent if (e.bKeyDown or (e.wVirtualKeyCode == VK_MENU and e.uChar.UnicodeChar)): return True return False def getwch(): handle = kernel32.GetStdHandle(STD_INPUT_HANDLE) old_mode = wintypes.DWORD() if not kernel32.GetConsoleMode(handle, ctypes.byref(old_mode)): raise ctypes.WinError(ctypes.get_last_error()) mode = old_mode.value & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT) kernel32.SetConsoleMode(handle, mode) try: