Skip to content

Instantly share code, notes, and snippets.

@njsmith
Last active April 11, 2024 02:33
Show Gist options
  • Save njsmith/211cce1d8583626dd945 to your computer and use it in GitHub Desktop.
Save njsmith/211cce1d8583626dd945 to your computer and use it in GitHub Desktop.
#!python
import sys
import time
import ctypes
import ctypes.wintypes
import subprocess
GetCurrentProcess = ctypes.windll.kernel32.GetCurrentProcess
GetCurrentProcess.argtypes = []
GetCurrentProcess.restype = ctypes.wintypes.HANDLE
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms724251%28v=vs.85%29.aspx
DuplicateHandle = ctypes.windll.kernel32.DuplicateHandle
DuplicateHandle.argtypes = [
# hSourceProcessHandle
ctypes.wintypes.HANDLE,
# hSourceHandle
ctypes.wintypes.HANDLE,
# hTargetProcessHandle
ctypes.wintypes.HANDLE,
# lpTargetHandle
ctypes.wintypes.LPHANDLE,
# dwDesiredAccess
ctypes.wintypes.DWORD,
# bInheritHandle
ctypes.wintypes.BOOL,
# dwOptions
ctypes.wintypes.DWORD,
]
DuplicateHandle.restype = ctypes.wintypes.BOOL
DUPLICATE_SAME_ACCESS = 0x2
WaitForSingleObject = ctypes.windll.kernel32.WaitForSingleProcess
WaitForSingleObject.argtypes = [ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD]
WaitForSingleObject.restype = ctypes.wintypes.DWORD
INFINITE = -1
CloseHandle = ctypes.windll.kernel32.CloseHandle
CloseHandle.argtypes = [ctypes.wintypes.HANDLE]
CloseHandle.restype = ctypes.wintypes.BOOL
if __name__ = "__main__":
if "child" not in sys.argv:
print("parent: starting")
h = ctypes.wintypes.HANDLE(-1)
me = GetCurrentProcess()
if not DuplicateHandle(me, me, me,
ctypes.byref(h),
0, # ignored for DUPLICATE_SAME_ACCESS
ctypes.wintypes.BOOL(True), # inheritable
DUPLICATE_SAME_ACCESS):
raise RuntimeError("failed to create inheritable process handle")
print("parent: created handle to parent process, value =", h.value)
print("parent: spawning child")
# We have an inheritable HANDLE pointing to the parent process; pass
# its value to the child so it can reconstitute it.
# In real life for pip's purposes, we'd probably want to instead have
# pip.exe spawn a call to something like:
# [sys.executable, "-m", "pip._windows_respawn", handle_value]
# + sys.argv[1:]
p = subprocess.Popen([sys.executable, __file__,
"child", str(h.value)])
print("parent: sleeping for 3 seconds...")
time.sleep(3)
print("parent: ...exiting")
else:
(_, child, parent_handle_str) = sys.argv
assert child == "child"
print("child: started, handle value is", parent_handle_str)
h = ctypes.wintypes.HANDLE(int(parent_handle_str))
print("child: waiting for parent to exit...")
WaitForSingleObject(h, INFINITE)
CloseHandle(h)
print("child: parent has exited -- I'm free!")
# now we are being run by python.exe, and our parent has exited, so we can safely write to our-parent.exe
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment