What process is using a given file? - python

I'm having trouble with one of my scripts, where it erratically seems to have trouble writing to its own log, throwing the error "This file is being used by another process."
I know there are ways to handle this with try excepts, but I'd like to find out why this is happening rather than just papering over it. Nothing else should be accessing that file at all. So in order to confirm the source of the bug, I'd like to find out what service is using that file.
Is there a way in Python on Windows to check what process is using a given file?

You can use Microsoft's handle.exe command-line utility. For example:
import re
import subprocess
_handle_pat = re.compile(r'(.*?)\s+pid:\s+(\d+).*[0-9a-fA-F]+:\s+(.*)')
def open_files(name):
"""return a list of (process_name, pid, filename) tuples for
open files matching the given name."""
lines = subprocess.check_output('handle.exe "%s"' % name).splitlines()
results = (_handle_pat.match(line.decode('mbcs')) for line in lines)
return [m.groups() for m in results if m]
Note that this has limitations regarding Unicode filenames. In Python 2 subprocess passes name as an ANSI string because it calls CreateProcessA instead of CreateProcessW. In Python 3 the name gets passed as Unicode. In either case, handle.exe writes its output using a lossy ANSI encoding, so the matched filename in the result tuple may contain best-fit characters and "?" replacements.

Please don't delete this answer in case I did anything wrong but give me a chance to correct it by leaving a comment. Thanks!
There is a better way than iterating through all PIDs (as was suggested in the comments) that involves performing a Windows API call to determine all handles on a given file. Please find below a code example which I have already posted for another question (however, cannot flag as duplicate since it does not have any accepted answers). Note that this only works for Windows.
import ctypes
from ctypes import wintypes
path = r"C:\temp\stackoverflow39570207.txt"
# -----------------------------------------------------------------------------
# generic strings and constants
# -----------------------------------------------------------------------------
ntdll = ctypes.WinDLL('ntdll')
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
NTSTATUS = wintypes.LONG
INVALID_HANDLE_VALUE = wintypes.HANDLE(-1).value
FILE_READ_ATTRIBUTES = 0x80
FILE_SHARE_READ = 1
OPEN_EXISTING = 3
FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
FILE_INFORMATION_CLASS = wintypes.ULONG
FileProcessIdsUsingFileInformation = 47
LPSECURITY_ATTRIBUTES = wintypes.LPVOID
ULONG_PTR = wintypes.WPARAM
# -----------------------------------------------------------------------------
# create handle on concerned file with dwDesiredAccess == FILE_READ_ATTRIBUTES
# -----------------------------------------------------------------------------
kernel32.CreateFileW.restype = wintypes.HANDLE
kernel32.CreateFileW.argtypes = (
wintypes.LPCWSTR, # In lpFileName
wintypes.DWORD, # In dwDesiredAccess
wintypes.DWORD, # In dwShareMode
LPSECURITY_ATTRIBUTES, # In_opt lpSecurityAttributes
wintypes.DWORD, # In dwCreationDisposition
wintypes.DWORD, # In dwFlagsAndAttributes
wintypes.HANDLE) # In_opt hTemplateFile
hFile = kernel32.CreateFileW(
path, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, None, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, None)
if hFile == INVALID_HANDLE_VALUE:
raise ctypes.WinError(ctypes.get_last_error())
# -----------------------------------------------------------------------------
# prepare data types for system call
# -----------------------------------------------------------------------------
class IO_STATUS_BLOCK(ctypes.Structure):
class _STATUS(ctypes.Union):
_fields_ = (('Status', NTSTATUS),
('Pointer', wintypes.LPVOID))
_anonymous_ = '_Status',
_fields_ = (('_Status', _STATUS),
('Information', ULONG_PTR))
iosb = IO_STATUS_BLOCK()
class FILE_PROCESS_IDS_USING_FILE_INFORMATION(ctypes.Structure):
_fields_ = (('NumberOfProcessIdsInList', wintypes.LARGE_INTEGER),
('ProcessIdList', wintypes.LARGE_INTEGER * 64))
info = FILE_PROCESS_IDS_USING_FILE_INFORMATION()
PIO_STATUS_BLOCK = ctypes.POINTER(IO_STATUS_BLOCK)
ntdll.NtQueryInformationFile.restype = NTSTATUS
ntdll.NtQueryInformationFile.argtypes = (
wintypes.HANDLE, # In FileHandle
PIO_STATUS_BLOCK, # Out IoStatusBlock
wintypes.LPVOID, # Out FileInformation
wintypes.ULONG, # In Length
FILE_INFORMATION_CLASS) # In FileInformationClass
# -----------------------------------------------------------------------------
# system call to retrieve list of PIDs currently using the file
# -----------------------------------------------------------------------------
status = ntdll.NtQueryInformationFile(hFile, ctypes.byref(iosb),
ctypes.byref(info),
ctypes.sizeof(info),
FileProcessIdsUsingFileInformation)
pidList = info.ProcessIdList[0:info.NumberOfProcessIdsInList]
print(pidList)

Related

how do we typecast a large C structure from a DLL to a Python ctypes.Structure

I've got a pointer to an XML document structure generated through libxml2. How do I cast it to my Python class xmlDoc(ctypes.Structure):?
Here's my Python code:
# libxml2 is a software library for parsing XML documents.
'''
This lib is used instead of the standard Python XML lib because it's using the regular libxml2 DLL,
which means documents and nodes can be shared with C++ objects directly.
'''
import ctypes
import sys
from PyQt5.QtWidgets import (
QApplication,
QFileDialog
)
import MessageBox as M
def xmlReadFile (filename, encoding, options):
# parse an XML file from the filesystem or the network.
# filename: a file or URL
# encoding: the document encoding, or NULL
# options: a combination of xmlParserOption
# Returns: the resulting document tree
# Load DLL into memory.
X = ctypes.WinDLL ("C:\\Users\\dholstein\\Documents\\XML\\lib\\libxml2.dll")
# set up prototype, first param is return value
XApiProto = ctypes.WINFUNCTYPE (ctypes.c_uint32, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_int32)
XApiParams = (1, "filname_", 0), (1, "encoding_", 0), (1, "options_", 0),
XApi = XApiProto (("xmlReadFile", X), XApiParams) # DLL function name
# data converted to ctypes and call
DocPtr = XApi ( ctypes.create_string_buffer (filename.encode('utf-8')),
ctypes.create_string_buffer (encoding.encode('utf-8')),
ctypes.c_int32 (options))
return DocPtr
class xmlDoc(ctypes.Structure):
_fields_ = [
("_private",ctypes.c_void_p), # application data
("type",ctypes.c_uint16), # XML_DOCUMENT_NODE, must be second !
("name",ctypes.c_char_p), # name/filename/URI of the document
("children",ctypes.c_void_p), # the document tree
("last",ctypes.c_void_p), # last child link
("parent",ctypes.c_void_p), # child->parent link
("next",ctypes.c_void_p), # next sibling link
("prev",ctypes.c_void_p), # previous sibling link
("doc",ctypes.c_void_p), # autoreference to itself End of common part
("compression",ctypes.c_int), # level of zlib compression
("standalone",ctypes.c_int), # standalone document (no external refs) 1 if standalone="yes" 0 if sta
("intSubset",ctypes.c_void_p), # the document internal subset
("extSubset",ctypes.c_void_p), # the document external subset
("oldNs",ctypes.c_void_p), # Global namespace, the old way
("version",ctypes.c_char_p), # the XML version string
("encoding",ctypes.c_char_p), # external initial encoding, if any
("ids",ctypes.c_void_p), # Hash table for ID attributes if any
("refs",ctypes.c_void_p), # Hash table for IDREFs attributes if any
("URL",ctypes.c_char_p), # The URI for that document
("charset",ctypes.c_int), # Internal flag for charset handling, actually an xmlCharEncoding
("dict",ctypes.c_void_p), # dict used to allocate names or NULL
("psvi",ctypes.c_void_p), # for type/PSVI information
("parseFlags",ctypes.c_int), # set of xmlParserOption used to parse the document
("properties",ctypes.c_int), # set of xmlDocProperties for this document set at the end of parsing
]
def test():
app = QApplication(sys.argv)
FileObj = QFileDialog.getOpenFileName(None, "Select XML file to load", None, "XML (*.xml)")
if len(FileObj[0]) > 0:
pDoc = xmlReadFile(FileObj[0], "UTF-8", 0)
# M.MessageBox(0, f"{pDoc:#0{10}x}", "XML doc ptr", 0)
D = ctypes.POINTER(xmlDoc)
ctypes.cast(pDoc, D)
print(D.name)
M.MessageBox(0, "fin", "Caption-y stuff", 0)
return
test()
As your comment says, the first parameter to the prototype is the return value. Instead of the incorrect c_uint32 which will truncate the returned pointer if on a 64-bit OS use POINTER(xmlDoc) and you won’t have to cast.
I would also use .argtypes and .restype instead of the more complicated prototype and params setup being used:
def xmlReadFile (filename, encoding, options):
X = ctypes.WinDLL ("C:\\Users\\dholstein\\Documents\\XML\\lib\\libxml2.dll")
X.xmlReadFile.restype = ctypes.POINTER(xmlDoc) # correct return type
X.xmlReadFile.argtypes = ctypes.c_char_p, ctypes.c_char_p, ctypes.c_int32
# wrapping in c_char_p or c_int32 isn't required because .argtypes are known.
pDoc = X.xmlReadFile(filename.encode(), encoding.encode(), options)
return DocPtr.contents # to dereference the pointer and return the structure.

How to add Python script to registry using Ctypes and kernel32.dll

I'm trying to add my program to registry and this is my code...
def regc():
reg = windll.kernel32
print(reg)
hkey = 'HKEY_CURRENT_USER'
lsubkey = 'Software\Microsoft\Windows\CurrentVersion\Run'
reserved = 0
flag = 'REG_OPTION_BACKUP_RESTORE'
samdesired = 'KEY_ALL_ACCESS'
ipsec = None
handle = reg.RegCreateKeyExA(hkey, lsubkey, reserved, flag, samdesired, ipsec, None)
Its not giving me any errors but it still isn't creating a new key in registry. What am I doing wrong?
To use ctypes correctly, define .argtypes and .restype to do error checking of your parameters. Many of the types used are wrong. hkey, flag, and samdesired are not strings, for example. The return value is not a handle, but a status. The return value is an output parameter (pkhResult in the docs). You must read the documentation and examine the header files of all the variable definitions carefully.
Also, in Python 3 strings are Unicode, so use the W form of Windows APIs to accept Unicode strings. Use raw strings (r'...') for the subkey since it contains backslashes that could be interpreted as escape codes.
Here's a working example:
from ctypes import *
from ctypes import wintypes as w
# Values found from reading RegCreateKeyExW documentation,
# using Go To Definition on the types in Visual Studio,
# and printing constants in a C program, e.g. printf("%lx\n",KEY_ALL_ACCESS);
HKEY = c_void_p
PHKEY = POINTER(HKEY)
REGSAM = w.DWORD
LPSECURITY_ATTRIBUTES = c_void_p
LSTATUS = w.LONG
# Disposition values
REG_CREATED_NEW_KEY = 0x00000001
REG_OPENED_EXISTING_KEY = 0x00000002
ERROR_SUCCESS = 0
HKEY_CURRENT_USER = c_void_p(0x80000001)
REG_OPTION_NON_VOLATILE = 0
KEY_ALL_ACCESS = 0x000F003F
dll = WinDLL('kernel32')
dll.RegCreateKeyExW.argtypes = HKEY,w.LPCWSTR,w.DWORD,w.LPWSTR,w.DWORD,REGSAM,LPSECURITY_ATTRIBUTES,PHKEY,w.LPDWORD
dll.RegCreateKeyExW.restype = LSTATUS
hkey = HKEY_CURRENT_USER
lsubkey = r'Software\Microsoft\Windows\CurrentVersion\Run'
options = REG_OPTION_NON_VOLATILE
samdesired = KEY_ALL_ACCESS
# Storage for output parameters...pass by reference.
handle = HKEY()
disp = w.DWORD()
status = dll.RegCreateKeyExW(HKEY_CURRENT_USER, lsubkey, 0, None, options, samdesired, None, byref(handle), byref(disp))
if status == ERROR_SUCCESS:
print(f'{disp=} {handle=}')
Output:
disp=c_ulong(2) handle=c_void_p(3460)
The disposition value of 2 indicates the key already exists (REG_OPENED_EXISTING_KEY).
You could also install pywin32 and use win32api.RegCreateKey or win32api.RegCreateKeyEx where all the work is already done for you.

Cancel a stalled file copy in python on windows

On windows, I want to copy a bunch of files over a network with Python. Sometimes, the network is not responding, and the copy is stalled. I want to check, if that happens, and skip the file in question, when that happens. By asking this related question here, I found out about the CopyFileEx function, that allows the use of a callback function, that can abort the file copy.
The implementation in Python looks like that:
import win32file
def Win32_CopyFileEx( ExistingFileName, NewFileName, Canc = False):
win32file.CopyFileEx(
ExistingFileName, # PyUNICODE | File to be copied
NewFileName, # PyUNICODE | Place to which it will be copied
Win32_CopyFileEx_ProgressRoutine, # CopyProgressRoutine | A python function that receives progress updates, can be None
Data = None, # object | An arbitrary object to be passed to the callback function
Cancel = Canc, # boolean | Pass True to cancel a restartable copy that was previously interrupted
CopyFlags = win32file.COPY_FILE_RESTARTABLE, # int | Combination of COPY_FILE_* flags
Transaction = None # PyHANDLE | Handle to a transaction as returned by win32transaction::CreateTransaction
)
From the documentation of the CopyFileEx function, I can see two possibilities of cancelation of a running copy.
pbCancel [in, optional] If this flag is set to TRUE during the copy operation, the operation is canceled. Otherwise, the copy
operation will continue to completion.
I could not figure out a way how to do that. I tried calling the same function with the same file handles again but with the cancel flag set to TRUE, but that leads in an error, because of the file in question being in use by another process.
Another possibility seems to be the callback function:
lpProgressRoutine [in, optional] The address of a callback function of
type LPPROGRESS_ROUTINE that is called each time another portion of
the file has been copied. This parameter can be NULL. For more
information on the progress callback function, see the
CopyProgressRoutine function.
The documentation of this ProgressRoutine states, that this callback is either called when the copy is started or when a junk of the file is finished copying. The callback function can cancel the copy process if it returns 1 or 2 ( cancel, stop). However, this callback function seems to not being called, when the copy of a junk is stalled.
So my question is: How I can cancel this copy on a per-file-basis when it is stalled?
win32file.CopyFileEx doesn't allow passing Cancel as anything but a boolean or integer value. In the API it's an LPBOOL pointer, which allows the caller to set its value concurrently in another thread. You'll have to use ctypes, Cython, or a C extension to get this level of control. Below I've written an example using ctypes.
If canceling the copy doesn't work because the thread is blocked on synchronous I/O, you can try calling CancelIoEx on the file handles that you're passed in the progress routine, or CancelSynchronousIo to cancel all synchronous I/O for the thread. These I/O cancel functions were added in Windows Vista. They're not available in Windows XP, in case you're still supporting it.
import ctypes
from ctypes import wintypes
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
COPY_FILE_FAIL_IF_EXISTS = 0x0001
COPY_FILE_RESTARTABLE = 0x0002
COPY_FILE_OPEN_SOURCE_FOR_WRITE = 0x0004
COPY_FILE_ALLOW_DECRYPTED_DESTINATION = 0x0008
COPY_FILE_COPY_SYMLINK = 0x0800
COPY_FILE_NO_BUFFERING = 0x1000
CALLBACK_CHUNK_FINISHED = 0
CALLBACK_STREAM_SWITCH = 1
PROGRESS_CONTINUE = 0
PROGRESS_CANCEL = 1
PROGRESS_STOP = 2
PROGRESS_QUIET = 3
ERROR_REQUEST_ABORTED = 0x04D3
if not hasattr(wintypes, 'LPBOOL'):
wintypes.LPBOOL = ctypes.POINTER(wintypes.BOOL)
def _check_bool(result, func, args):
if not result:
raise ctypes.WinError(ctypes.get_last_error())
return args
LPPROGRESS_ROUTINE = ctypes.WINFUNCTYPE(
wintypes.DWORD, # _Retval_
wintypes.LARGE_INTEGER, # _In_ TotalFileSize
wintypes.LARGE_INTEGER, # _In_ TotalBytesTransferred
wintypes.LARGE_INTEGER, # _In_ StreamSize
wintypes.LARGE_INTEGER, # _In_ StreamBytesTransferred
wintypes.DWORD, # _In_ dwStreamNumber
wintypes.DWORD, # _In_ dwCallbackReason
wintypes.HANDLE, # _In_ hSourceFile
wintypes.HANDLE, # _In_ hDestinationFile
wintypes.LPVOID) # _In_opt_ lpData
kernel32.CopyFileExW.errcheck = _check_bool
kernel32.CopyFileExW.argtypes = (
wintypes.LPCWSTR, # _In_ lpExistingFileName
wintypes.LPCWSTR, # _In_ lpNewFileName
LPPROGRESS_ROUTINE, # _In_opt_ lpProgressRoutine
wintypes.LPVOID, # _In_opt_ lpData
wintypes.LPBOOL, # _In_opt_ pbCancel
wintypes.DWORD) # _In_ dwCopyFlags
#LPPROGRESS_ROUTINE
def debug_progress(tsize, ttrnsfr, stsize, sttrnsfr, stnum, reason,
hsrc, hdst, data):
print('ttrnsfr: %d, stnum: %d, stsize: %d, sttrnsfr: %d, reason: %d' %
(ttrnsfr, stnum, stsize, sttrnsfr, reason))
return PROGRESS_CONTINUE
def copy_file(src, dst, cancel=None, flags=0,
cbprogress=None, data=None):
if isinstance(cancel, int):
cancel = ctypes.byref(wintypes.BOOL(cancel))
elif cancel is not None:
cancel = ctypes.byref(cancel)
if cbprogress is None:
cbprogress = LPPROGRESS_ROUTINE()
kernel32.CopyFileExW(src, dst, cbprogress, data, cancel, flags)
Example
if __name__ == '__main__':
import os
import tempfile
import threading
src_fd, src = tempfile.mkstemp()
os.write(src_fd, os.urandom(16 * 2 ** 20))
os.close(src_fd)
dst = tempfile.mktemp()
cancel = wintypes.BOOL(False)
t = threading.Timer(0.001, type(cancel).value.__set__, (cancel, True))
t.start()
try:
copy_file(src, dst, cancel, cbprogress=debug_progress)
except OSError as e:
print(e)
assert e.winerror == ERROR_REQUEST_ABORTED
finally:
if os.path.exists(src):
os.remove(src)
if os.path.exists(dst):
os.remove(dst)

Monitoring memory addresses in Python [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I need to know how to monitor a memory address and its values in Python.
For example: I have a game that is written in C. I want to write a Python script that read the memory address of my current HitPoints and take actions based on its values.
I already can get the memory addresses with CheatEngine, but I don't know how use this in Python.
Here's a read_process function. The result is either bytes (2.x str), or an array of ctypes structures. The default is to read 1 byte from the process. The optional dtype parameter must be a ctypes type, such as ctypes.c_cint or a ctypes.Structure subclass. It reads an array of the given type and length.
Be careful to avoid dereferencing pointer values. For example, if you pass dtype=c_char_p, then simply indexing the result array will try to dereference a remote pointer in the current process, which will likely crash Python. In a previous answer I wrote a read-only RemotePointer class if you need to handle that case.
ctypes definitions
import ctypes
from ctypes import wintypes
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
PROCESS_VM_READ = 0x0010
SIZE_T = ctypes.c_size_t
PSIZE_T = ctypes.POINTER(SIZE_T)
def _check_bool(result, func, args):
if not result:
raise ctypes.WinError(error)
return args
kernel32.OpenProcess.errcheck = _check_bool
kernel32.OpenProcess.restype = wintypes.HANDLE
kernel32.OpenProcess.argtypes = (
wintypes.DWORD, # _In_ dwDesiredAccess
wintypes.BOOL, # _In_ bInheritHandle
wintypes.DWORD) # _In_ dwProcessId
kernel32.CloseHandle.errcheck = _check_bool
kernel32.CloseHandle.argtypes = (
wintypes.HANDLE,)
kernel32.ReadProcessMemory.errcheck = _check_bool
kernel32.ReadProcessMemory.argtypes = (
wintypes.HANDLE, # _In_ hProcess
wintypes.LPCVOID, # _In_ lpBaseAddress
wintypes.LPVOID, # _Out_ lpBuffer
SIZE_T, # _In_ nSize
PSIZE_T) # _Out_ lpNumberOfBytesRead
read_process definition
def read_process(pid, address, length=1, dtype=ctypes.c_char):
result = (dtype * length)()
nread = SIZE_T()
hProcess = kernel32.OpenProcess(PROCESS_VM_READ, False, pid)
try:
kernel32.ReadProcessMemory(hProcess, address, result,
ctypes.sizeof(result),
ctypes.byref(nread))
finally:
kernel32.CloseHandle(hProcess)
if issubclass(dtype, ctypes.c_char):
return result.raw
return result
example
if __name__ == '__main__':
import os
class DType(ctypes.Structure):
_fields_ = (('x', ctypes.c_int),
('y', ctypes.c_double))
source = (DType * 2)(*[(42, 3.14),
(84, 2.72)])
pid = os.getpid()
address = ctypes.addressof(source)
sink = read_process(pid, address, 2, DType)
for din, dout in zip(source, sink):
assert din.x == dout.x
assert din.y == dout.y
size = ctypes.sizeof(source)
buf_source = ctypes.string_at(source, size)
buf_sink = read_process(pid, address, size)
assert buf_source == buf_sink

Python - ctypes and mutable buffers

I'm trying to work with ctypes, and I can't get the call to FormatMessage() to work properly.
Here's the code I have so far; I think the only issue is passing in a mutable buffer; I'm getting an ArgumentError from ctypes about lpBuffer
import ctypes
from ctypes.wintypes import DWORD
def main():
fm = ctypes.windll.kernel32.FormatMessageA
fm.argtypes = [DWORD,DWORD,DWORD,DWORD,ctypes.wintypes.LPWSTR(),DWORD]
dwFlags = DWORD(0x1000) # FORMAT_MESSAGE_ALLOCATE_BUFFER |FORMAT_MESSAGE_FROM_SYSTEM
lpSource = DWORD(0)
dwMessageId = DWORD(0x05)
dwLanguageId = DWORD(0)
#buf = ctypes.wintypes.LPWSTR()
#lpBuffer = ctypes.byref(buf)
lpBuffer = ctypes.create_string_buffer(512)
nSize = DWORD(512)
res = fm(dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize)
print res
I'm getting an error on the lpBuffer argument saying it's a wrong type, but I've tried as many variations of passing in the buffer as I could think of. I've tried doing it similar to here: https://gist.github.com/CBWhiz/6135237 and setting FORMAT_MESSAGE_ALLOCATE_BUFFER then passing in a LPWSTR() byref, I've also tried changing the argtype, pointer and casting to a variety of LPWSTR(), c_char_p, etc, but no matter what I do it keeps complaining.
What's the proper syntax to get the function to execute properly? I know ctypes can be finnicky but I haven't found anything in the documentation to resolve the issue (I know the documentation uses prototype() but I'd like to do it this way for now)
Thanks
Here's the argtypes definition for FormatMessageW (note "W" for Unicode):
import ctypes
from ctypes import wintypes
fm = ctypes.windll.kernel32.FormatMessageW
fm.argtypes = [
wintypes.DWORD, # dwFlags
wintypes.LPCVOID, # lpSource
wintypes.DWORD, # dwMessageId
wintypes.DWORD, # dwLanguageId
wintypes.LPWSTR, # lpBuffer
wintypes.DWORD, # nSize
wintypes.LPVOID, # Arguments (va_list *)
]
FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x100
FORMAT_MESSAGE_FROM_SYSTEM = 0x1000
If FormatMessage allocates the buffer, you have to instead pass a reference to lpBuffer. Just cast the reference to get around the TypeError. Also, remember to call kernel32.LocalFree to free the buffer:
def main():
dwFlags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER
lpSource = None
dwMessageId = 5
dwLanguageId = 0
lpBuffer = wintypes.LPWSTR()
nSize = 0 # minimum size
Arguments = None
if not fm(dwFlags, lpSource, dwMessageId, dwLanguageId,
ctypes.cast(ctypes.byref(lpBuffer), wintypes.LPWSTR),
nSize, Arguments):
raise ctypes.WinError()
msg = lpBuffer.value.rstrip()
ctypes.windll.kernel32.LocalFree(lpBuffer)
return msg

Categories

Resources