使用找到的代码here,可以成为备用用户成功启动应用程序。但是,在应用程序启动后,Python崩溃,并且Windows显示“python.exe已停止工作”。它似乎只发生在函数调用完成后,但似乎不是由函数中的任何内容引起的。
import ctypes, sys
from ctypes import Structure, sizeof
NULL = 0
TRUE = 1
FALSE = 0
INVALID_HANDLE_VALUE = -1
WORD = ctypes.c_ushort
DWORD = ctypes.c_uint
LPSTR = ctypes.c_char_p
LPBYTE = LPSTR
HANDLE = DWORD
# typedef struct _PROCESS_INFORMATION {
# HANDLE hProcess;
# HANDLE hThread;
# DWORD dwProcessId;
# DWORD dwThreadId;
# } PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;
class PROCESS_INFORMATION(Structure):
_pack_ = 1
_fields_ = [
('hProcess', HANDLE),
('hThread', HANDLE),
('dwProcessId', DWORD),
('dwThreadId', DWORD),
]
# typedef struct _STARTUPINFO {
# DWORD cb;
# LPSTR lpReserved;
# LPSTR lpDesktop;
# LPSTR lpTitle;
# DWORD dwX;
# DWORD dwY;
# DWORD dwXSize;
# DWORD dwYSize;
# DWORD dwXCountChars;
# DWORD dwYCountChars;
# DWORD dwFillAttribute;
# DWORD dwFlags;
# WORD wShowWindow;
# WORD cbReserved2;
# LPBYTE lpReserved2;
# HANDLE hStdInput;
# HANDLE hStdOutput;
# HANDLE hStdError;
# } STARTUPINFO, *LPSTARTUPINFO;
class STARTUPINFO(Structure):
_pack_ = 1
_fields_ = [
('cb', DWORD),
('lpReserved', DWORD), # LPSTR
('lpDesktop', LPSTR),
('lpTitle', LPSTR),
('dwX', DWORD),
('dwY', DWORD),
('dwXSize', DWORD),
('dwYSize', DWORD),
('dwXCountChars', DWORD),
('dwYCountChars', DWORD),
('dwFillAttribute', DWORD),
('dwFlags', DWORD),
('wShowWindow', WORD),
('cbReserved2', WORD),
('lpReserved2', DWORD), # LPBYTE
('hStdInput', DWORD),
('hStdOutput', DWORD),
('hStdError', DWORD),
]
# BOOL WINAPI CreateProcessWithLogonW(
# __in LPCWSTR lpUsername,
# __in_opt LPCWSTR lpDomain,
# __in LPCWSTR lpPassword,
# __in DWORD dwLogonFlags,
# __in_opt LPCWSTR lpApplicationName,
# __inout_opt LPWSTR lpCommandLine,
# __in DWORD dwCreationFlags,
# __in_opt LPVOID lpEnvironment,
# __in_opt LPCWSTR lpCurrentDirectory,
# __in LPSTARTUPINFOW lpStartupInfo,
# __out LPPROCESS_INFORMATION lpProcessInfo
# );
def CreateProcessWithLogonW(lpUsername = None, lpDomain = None, lpPassword =
None, dwLogonFlags = 0, lpApplicationName = None, lpCommandLine = None,
dwCreationFlags = 0, lpEnvironment = None, lpCurrentDirectory = None,
lpStartupInfo = None):
if not lpUsername:
lpUsername = NULL
else:
lpUsername = ctypes.c_wchar_p(lpUsername)
if not lpDomain:
lpDomain = NULL
else:
lpDomain = ctypes.c_wchar_p(lpDomain)
if not lpPassword:
lpPassword = NULL
else:
lpPassword = ctypes.c_wchar_p(lpPassword)
if not lpApplicationName:
lpApplicationName = NULL
else:
lpApplicationName = ctypes.c_wchar_p(lpApplicationName)
if not lpCommandLine:
lpCommandLine = NULL
else:
lpCommandLine = ctypes.create_unicode_buffer(lpCommandLine)
if not lpEnvironment:
lpEnvironment = NULL
else:
lpEnvironment = ctypes.c_wchar_p(lpEnvironment)
if not lpCurrentDirectory:
lpCurrentDirectory = NULL
else:
lpCurrentDirectory = ctypes.c_wchar_p(lpCurrentDirectory)
if not lpStartupInfo:
lpStartupInfo = STARTUPINFO()
lpStartupInfo.cb = sizeof(STARTUPINFO)
lpStartupInfo.lpReserved = 0
lpStartupInfo.lpDesktop = 0
lpStartupInfo.lpTitle = 0
lpStartupInfo.dwFlags = 0
lpStartupInfo.cbReserved2 = 0
lpStartupInfo.lpReserved2 = 0
lpProcessInformation = PROCESS_INFORMATION()
lpProcessInformation.hProcess = INVALID_HANDLE_VALUE
lpProcessInformation.hThread = INVALID_HANDLE_VALUE
lpProcessInformation.dwProcessId = 0
lpProcessInformation.dwThreadId = 0
success = ctypes.windll.advapi32.CreateProcessWithLogonW(lpUsername,
lpDomain, lpPassword, dwLogonFlags, lpApplicationName,
ctypes.byref(lpCommandLine), dwCreationFlags, lpEnvironment,
lpCurrentDirectory, ctypes.byref(lpStartupInfo),
ctypes.byref(lpProcessInformation))
if success == FALSE:
raise ctypes.WinError()
#A raw_input or other blocking function here will prevent python from crashing until continuing
return lpProcessInformation #Happens whether or not this is returned
CreateProcessWithLogonW("User", "Domain", "Password", 0, None, "C:\\Windows\\notepad.exe")
print("Test") #This will never be reached
正如我在代码中所评论的那样,如果阻止函数结束,则不会发生崩溃。无法达到范围返回到函数外部的任何内容,并且python.exe将崩溃。
我尝试过的解决方法是在函数末尾使用taskkill来通过PID来终止python.exe进程。这确实可以防止错误消息按预期发生,但不太理想,因为它还会杀死任何子进程(包括成功启动的进程)。我无法弄清楚为什么完成函数调用会导致Python崩溃。这在Python 2.7和3.x中都会发生。非常感谢任何建议。
答案 0 :(得分:3)
对于DWORD
或任何其他指针类型,使用32位HANDLE
在64位Windows上不正确。 ctypes.wintypes
模块定义了适用于32位和64位Windows的类型。如果它缺少特定类型,您可以在Windows Data Types中找到该定义。
设置_pack_ = 1
错误地使用1字节对齐而不是使用本机对齐填充。此外,STARTUPINFOW
应使用LPWSTR
代替LPSTR
。
尝试重写:
import ctypes
from ctypes import wintypes
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
advapi32 = ctypes.WinDLL('advapi32', use_last_error=True)
CREATE_NEW_CONSOLE = 0x00000010
CREATE_NO_WINDOW = 0x08000000
DETACHED_PROCESS = 0x00000008
CREATE_NEW_PROCESS_GROUP = 0x00000200
CREATE_UNICODE_ENVIRONMENT = 0x00000400
if not hasattr(wintypes, 'LPBYTE'):
wintypes.LPBYTE = ctypes.POINTER(wintypes.BYTE)
class HANDLE(wintypes.HANDLE):
def detach(self):
handle, self.value = self.value, None
return wintypes.HANDLE(handle)
def close(self, CloseHandle=kernel32.CloseHandle):
if self:
CloseHandle(self.detach())
def __del__(self):
self.close()
class PROCESS_INFORMATION(ctypes.Structure):
"""http://msdn.microsoft.com/en-us/library/ms684873"""
_fields_ = (('hProcess', HANDLE),
('hThread', HANDLE),
('dwProcessId', wintypes.DWORD),
('dwThreadId', wintypes.DWORD))
LPPROCESS_INFORMATION = ctypes.POINTER(PROCESS_INFORMATION)
class STARTUPINFOW(ctypes.Structure):
"""http://msdn.microsoft.com/en-us/library/ms686331"""
_fields_ = (('cb', wintypes.DWORD),
('lpReserved', wintypes.LPWSTR),
('lpDesktop', wintypes.LPWSTR),
('lpTitle', wintypes.LPWSTR),
('dwX', wintypes.DWORD),
('dwY', wintypes.DWORD),
('dwXSize', wintypes.DWORD),
('dwYSize', wintypes.DWORD),
('dwXCountChars', wintypes.DWORD),
('dwYCountChars', wintypes.DWORD),
('dwFillAttribute', wintypes.DWORD),
('dwFlags', wintypes.DWORD),
('wShowWindow', wintypes.WORD),
('cbReserved2', wintypes.WORD),
('lpReserved2', wintypes.LPBYTE),
('hStdInput', wintypes.HANDLE),
('hStdOutput', wintypes.HANDLE),
('hStdError', wintypes.HANDLE))
def __init__(self, *args, **kwds):
self.cb = ctypes.sizeof(self)
super(STARTUPINFOW, self).__init__(*args, **kwds)
LPSTARTUPINFOW = ctypes.POINTER(STARTUPINFOW)
def _check_bool(result, func, args):
if not result:
raise ctypes.WinError(ctypes.get_last_error())
return args
# http://msdn.microsoft.com/en-us/library/ms682431
advapi32.CreateProcessWithLogonW.errcheck = _check_bool
advapi32.CreateProcessWithLogonW.argtypes = (
wintypes.LPCWSTR, # lpUsername
wintypes.LPCWSTR, # lpDomain
wintypes.LPCWSTR, # lpPassword
wintypes.DWORD, # dwLogonFlags
wintypes.LPCWSTR, # lpApplicationName
wintypes.LPWSTR, # lpCommandLine (inout)
wintypes.DWORD, # dwCreationFlags
wintypes.LPCWSTR, # lpEnvironment (force Unicode)
wintypes.LPCWSTR, # lpCurrentDirectory
LPSTARTUPINFOW, # lpStartupInfo
LPPROCESS_INFORMATION) # lpProcessInfo (out)
def CreateProcessWithLogonW(username, password, domain=None, logonflags=0,
executable=None, commandline=None, creationflags=0,
env=None, cwd=None, startupinfo=None):
if commandline is not None:
commandline = ctypes.create_unicode_buffer(commandline)
creationflags |= CREATE_UNICODE_ENVIRONMENT
if startupinfo is None:
startupinfo = STARTUPINFOW()
pi = PROCESS_INFORMATION()
advapi32.CreateProcessWithLogonW(username, domain, password, logonflags,
executable, commandline, creationflags,
env, cwd, ctypes.byref(startupinfo),
ctypes.byref(pi))
return pi.hProcess, pi.hThread, pi.dwProcessId, pi.dwThreadId
if __name__ == '__main__':
import os
import getpass
username = input('username: ')
password = getpass.getpass('password: ')
exe = os.environ['ComSpec']
cflags = CREATE_NEW_CONSOLE
hProcess, hThread, pid, tid = CreateProcessWithLogonW(
username, password, executable=exe, creationflags=cflags)
print('PID: %d' % pid)