首页 > 解决方案 > 如何从 Windows 服务锁定窗口?

问题描述

我正在用 Python 编写一个 Windows 服务,如果发生特定情况(例如,有人在一段时间内无人看管电脑),它有时需要锁定窗口。我找到的解决方案是user32.LockWorkStation()使用该ctypes模块。

ctypes.windll.user32.LockWorkStation()

但是,在锁定操作系统失败后,我注意到 LockWorkstation 专门在 Windows 服务不支持的交互式 [桌面] 模式下工作。它实际上浪费了我很多时间,因为它在调试时起作用!反正,

引用LockWorkStation 函数

LockWorkStation 函数只能由交互式桌面上运行的进程调用。此外,用户必须登录,并且工作站不能已经被锁定。

windows服务确实有一个属性,您可以允许它与桌面交互(在登录选项卡上),但是,如果我没记错的话,这个功能是在最新版本的windows上禁用的,除此之外,不是一个好启用它的想法。

还引用MSDN

服务没有消息循环,除非它们被允许与桌面交互。如果消息循环不是由隐藏表单提供的,如本例,服务必须在本地系统帐户下运行,并且需要手动干预才能与桌面交互。也就是说,管理员必须手动选中服务属性对话框的登录选项卡上的允许服务与桌面交互复选框。在这种情况下,会自动提供消息循环。仅当服务在本地系统帐户下运行时,此选项才可用。无法以编程方式启用与桌面的交互。

因此,我正在寻找其他解决方案,可以让我从 Windows 服务中锁定 Windows。我怎样才能做到这一点?

标签: pythonpywin32

解决方案


概括

为了与服务中的用户会话进行交互,您首先需要使用用户会话 ID。基本上你需要使用WTSGetActiveConsoleSessionId,WTSGetActiveConsoleSessionId之前CreateEnvironmentBlock调用CreateProcessAsUser. 这是可以解决问题的代码段:

import win32process
import win32con
import win32ts

console_session_id = win32ts.WTSGetActiveConsoleSessionId()
console_user_token = win32ts.WTSQueryUserToken(console_session_id)
startup = win32process.STARTUPINFO()
priority = win32con.NORMAL_PRIORITY_CLASS
environment = win32profile.CreateEnvironmentBlock(console_user_token, False)
handle, thread_id ,pid, tid = win32process.CreateProcessAsUser(console_user_token, None, "rundll32.exe user32.dll,LockWorkStation", None, None, True, priority, environment, None, startup)

如果你需要调用一个特定的应用程序,你可以这样调用:

win32process.CreateProcessAsUser(console_user_token, your_app_exe, app_args, None, None, True, priority, environment, None, startup)

这实际上是 Windows 中的服务与用户会话交互的方式。使用此方法,您不再需要用户凭据。

长解释:

当涉及到 Windows 服务访问/与用户会话交互时(会话 >0)。通常建议使用 CreateProcessAsUser()。一个人会继续做这样的事情(参考):

user = "username"
pword = "123456"
domain = "." # means current domain

logontype = win32con.LOGON32_LOGON_INTERACTIVE
# some may suggest to use BATCH mode instead in case you fail! but this doesn't work either!
# logontype = win32con.LOGON32_LOGON_BATCH
provider = win32con.LOGON32_PROVIDER_WINNT50
token = win32security.LogonUser(user, domain, pword, logontype, provider)

startup = win32process.STARTUPINFO()
process_information = PROCESS_INFORMATION()
cwd = os.path.dirname(__file__)
lock_file = os.path.join(cwd,'system_locker.exe')
appname = lock_file 
priority = win32con.NORMAL_PRIORITY_CLASS
result = win32process.CreateProcessAsUser(token, appname, None, None, None, True, priority, None, None, startup)

但如果你这样做,你将面临错误:

(1314, 'CreateProcessAsUser', 'A required privilege is not held by the client.')

有很多建议可以解决这个问题,比如禁用 UAC。但没有一个会起作用,如果您查看有关CreateProcessAsUser的MSDN文档,您会看到:

通常,调用 CreateProcessAsUser 函数的进程必须具有 SE_INCREASE_QUOTA_NAME 权限,并且如果令牌不可分配,则可能需要 SE_ASSIGNPRIMARYTOKEN_NAME 权限。如果此函数因 ERROR_PRIVILEGE_NOT_HELD (1314) 而失败,请改用 CreateProcessWithLogonW 函数。CreateProcessWithLogonW 不需要特殊权限,但必须允许指定的用户帐户以交互方式登录。通常,最好使用 CreateProcessWithLogonW 创建具有备用凭据的进程。

这意味着使用CreateProcessWithLogonW. 如果有人去尝试这个,例如这样:

from ctypes import *
from ctypes.wintypes import *

INVALID_HANDLE_VALUE = -1
CREATE_UNICODE_ENVIRONMENT = 0x00000400

CData = Array.__base__
LPBYTE = POINTER(BYTE)


class PROCESS_INFORMATION(Structure):
    '''http://msdn.microsoft.com/en-us/library/ms684873'''
    _fields_ = [
        ('hProcess', HANDLE),
        ('hThread', HANDLE),
        ('dwProcessId', DWORD),
        ('dwThreadId', DWORD),
    ]


LPPROCESS_INFORMATION = POINTER(PROCESS_INFORMATION)


class STARTUPINFOW(Structure):
    'http://msdn.microsoft.com/en-us/library/ms686331'
    _fields_ = [
        ('cb', DWORD),
        ('lpReserved', LPWSTR),
        ('lpDesktop', LPWSTR),
        ('lpTitle', LPWSTR),
        ('dwX', DWORD),
        ('dwY', DWORD),
        ('dwXSize', DWORD),
        ('dwYSize', DWORD),
        ('dwXCountChars', DWORD),
        ('dwYCountChars', DWORD),
        ('dwFillAttribute', DWORD),
        ('dwFlags', DWORD),
        ('wShowWindow', WORD),
        ('cbReserved2', WORD),
        ('lpReserved2', LPBYTE),
        ('hStdInput', HANDLE),
        ('hStdOutput', HANDLE),
        ('hStdError', HANDLE),
    ]


LPSTARTUPINFOW = POINTER(STARTUPINFOW)


# http://msdn.microsoft.com/en-us/library/ms682431
windll.advapi32.CreateProcessWithLogonW.restype = BOOL
windll.advapi32.CreateProcessWithLogonW.argtypes = [
                                                    LPCWSTR,  # lpUsername
                                                    LPCWSTR,  # lpDomain
                                                    LPCWSTR,  # lpPassword
                                                    DWORD,  # dwLogonFlags
                                                    LPCWSTR,  # lpApplicationName
                                                    LPWSTR,  # lpCommandLine (inout)
                                                    DWORD,  # dwCreationFlags
                                                    LPCWSTR,  # lpEnvironment  (force Unicode)
                                                    LPCWSTR,  # lpCurrentDirectory
                                                    LPSTARTUPINFOW,  # lpStartupInfo
                                                    LPPROCESS_INFORMATION,  # lpProcessInfo (out)
                                                ]


def CreateProcessWithLogonW(
                            lpUsername=None,
                            lpDomain=None,
                            lpPassword=None,
                            dwLogonFlags=0,
                            lpApplicationName=None,
                            lpCommandLine=None,
                            dwCreationFlags=0,
                            lpEnvironment=None,
                            lpCurrentDirectory=None,
                            startupInfo=None
):
    if (lpCommandLine is not None and
            not isinstance(lpCommandLine, CData)
        ):
        lpCommandLine = create_unicode_buffer(lpCommandLine)
    dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT
    if startupInfo is None:
        startupInfo = STARTUPINFOW(sizeof(STARTUPINFOW))
    processInformation = PROCESS_INFORMATION(
        INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE)
    success = windll.advapi32.CreateProcessWithLogonW(
        lpUsername, lpDomain, lpPassword, dwLogonFlags, lpApplicationName,
        lpCommandLine, dwCreationFlags, lpEnvironment, lpCurrentDirectory,
        byref(startupInfo), byref(processInformation))
    if not success:
        raise WinError()
    return processInformation

....

result = CreateProcessWithLogonW(user, domain, pword, 0, None, "rundll32.exe user32.dll,LockWorkStation")

他/她将面临错误:

(13, 'Access is denied.', None, 5)

另一个失败的类似实现如下(取自


from ctypes import wintypes
from subprocess import PIPE

kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
advapi32 = ctypes.WinDLL('advapi32', use_last_error=True)

ERROR_INVALID_HANDLE = 0x0006
INVALID_HANDLE_VALUE = wintypes.HANDLE(-1).value
INVALID_DWORD_VALUE = wintypes.DWORD(-1).value

DEBUG_PROCESS                    = 0x00000001
DEBUG_ONLY_THIS_PROCESS          = 0x00000002
CREATE_SUSPENDED                 = 0x00000004
DETACHED_PROCESS                 = 0x00000008
CREATE_NEW_CONSOLE               = 0x00000010
CREATE_NEW_PROCESS_GROUP         = 0x00000200
CREATE_UNICODE_ENVIRONMENT       = 0x00000400
CREATE_SEPARATE_WOW_VDM          = 0x00000800
CREATE_SHARED_WOW_VDM            = 0x00001000
INHERIT_PARENT_AFFINITY          = 0x00010000
CREATE_PROTECTED_PROCESS         = 0x00040000
EXTENDED_STARTUPINFO_PRESENT     = 0x00080000
CREATE_BREAKAWAY_FROM_JOB        = 0x01000000
CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000
CREATE_DEFAULT_ERROR_MODE        = 0x04000000
CREATE_NO_WINDOW                 = 0x08000000

STARTF_USESHOWWINDOW    = 0x00000001
STARTF_USESIZE          = 0x00000002
STARTF_USEPOSITION      = 0x00000004
STARTF_USECOUNTCHARS    = 0x00000008
STARTF_USEFILLATTRIBUTE = 0x00000010
STARTF_RUNFULLSCREEN    = 0x00000020
STARTF_FORCEONFEEDBACK  = 0x00000040
STARTF_FORCEOFFFEEDBACK = 0x00000080
STARTF_USESTDHANDLES    = 0x00000100
STARTF_USEHOTKEY        = 0x00000200
STARTF_TITLEISLINKNAME  = 0x00000800
STARTF_TITLEISAPPID     = 0x00001000
STARTF_PREVENTPINNING   = 0x00002000

SW_HIDE            = 0
SW_SHOWNORMAL      = 1
SW_SHOWMINIMIZED   = 2
SW_SHOWMAXIMIZED   = 3
SW_SHOWNOACTIVATE  = 4
SW_SHOW            = 5
SW_MINIMIZE        = 6
SW_SHOWMINNOACTIVE = 7
SW_SHOWNA          = 8
SW_RESTORE         = 9
SW_SHOWDEFAULT     = 10 # ~STARTUPINFO
SW_FORCEMINIMIZE   = 11

LOGON_WITH_PROFILE        = 0x00000001
LOGON_NETCREDENTIALS_ONLY = 0x00000002

STD_INPUT_HANDLE  = wintypes.DWORD(-10).value
STD_OUTPUT_HANDLE = wintypes.DWORD(-11).value
STD_ERROR_HANDLE  = wintypes.DWORD(-12).value

class HANDLE(wintypes.HANDLE):
    __slots__ = 'closed',

    def __int__(self):
        return self.value or 0

    def Detach(self):
        if not getattr(self, 'closed', False):
            self.closed = True
            value = int(self)
            self.value = None
            return value
        raise ValueError("already closed")

    def Close(self, CloseHandle=kernel32.CloseHandle):
        if self and not getattr(self, 'closed', False):
            CloseHandle(self.Detach())

    __del__ = Close

    def __repr__(self):
        return "%s(%d)" % (self.__class__.__name__, int(self))

class PROCESS_INFORMATION(ctypes.Structure):
    """https://msdn.microsoft.com/en-us/library/ms684873"""
    __slots__ = '_cached_hProcess', '_cached_hThread'

    _fields_ = (('_hProcess',   HANDLE),
                ('_hThread',    HANDLE),
                ('dwProcessId', wintypes.DWORD),
                ('dwThreadId',  wintypes.DWORD))

    @property
    def hProcess(self):
        if not hasattr(self, '_cached_hProcess'):
            self._cached_hProcess = self._hProcess
        return self._cached_hProcess

    @property
    def hThread(self):
        if not hasattr(self, '_cached_hThread'):
            self._cached_hThread = self._hThread
        return self._cached_hThread

    def __del__(self):
        try:
            self.hProcess.Close()
        finally:
            self.hThread.Close()

LPPROCESS_INFORMATION = ctypes.POINTER(PROCESS_INFORMATION)

LPBYTE = ctypes.POINTER(wintypes.BYTE)

class STARTUPINFO(ctypes.Structure):
    """https://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',     LPBYTE),
                ('hStdInput',       wintypes.HANDLE),
                ('hStdOutput',      wintypes.HANDLE),
                ('hStdError',       wintypes.HANDLE))

    def __init__(self, **kwds):
        self.cb = ctypes.sizeof(self)
        super(STARTUPINFO, self).__init__(**kwds)

class PROC_THREAD_ATTRIBUTE_LIST(ctypes.Structure):
    pass

PPROC_THREAD_ATTRIBUTE_LIST = ctypes.POINTER(PROC_THREAD_ATTRIBUTE_LIST)

class STARTUPINFOEX(STARTUPINFO):
    _fields_ = (('lpAttributeList', PPROC_THREAD_ATTRIBUTE_LIST),)

LPSTARTUPINFO = ctypes.POINTER(STARTUPINFO)
LPSTARTUPINFOEX = ctypes.POINTER(STARTUPINFOEX)

class SECURITY_ATTRIBUTES(ctypes.Structure):
    _fields_ = (('nLength',              wintypes.DWORD),
                ('lpSecurityDescriptor', wintypes.LPVOID),
                ('bInheritHandle',       wintypes.BOOL))
    def __init__(self, **kwds):
        self.nLength = ctypes.sizeof(self)
        super(SECURITY_ATTRIBUTES, self).__init__(**kwds)

LPSECURITY_ATTRIBUTES = ctypes.POINTER(SECURITY_ATTRIBUTES)

class HANDLE_IHV(HANDLE):
    pass

class DWORD_IDV(wintypes.DWORD):
    pass

def _check_ihv(result, func, args):
    if result.value == INVALID_HANDLE_VALUE:
        raise ctypes.WinError(ctypes.get_last_error())
    return result.value

def _check_idv(result, func, args):
    if result.value == INVALID_DWORD_VALUE:
        raise ctypes.WinError(ctypes.get_last_error())
    return result.value

def _check_bool(result, func, args):
    if not result:
        raise ctypes.WinError(ctypes.get_last_error())
    return args

def WIN(func, restype, *argtypes):
    func.restype = restype
    func.argtypes = argtypes
    if issubclass(restype, HANDLE_IHV):
        func.errcheck = _check_ihv
    elif issubclass(restype, DWORD_IDV):
        func.errcheck = _check_idv
    else:
        func.errcheck = _check_bool

# https://msdn.microsoft.com/en-us/library/ms724211
WIN(kernel32.CloseHandle, wintypes.BOOL,
    wintypes.HANDLE,) # _In_ HANDLE hObject

# https://msdn.microsoft.com/en-us/library/ms685086
WIN(kernel32.ResumeThread, DWORD_IDV,
    wintypes.HANDLE,) # _In_ hThread

# https://msdn.microsoft.com/en-us/library/ms682425
WIN(kernel32.CreateProcessW, wintypes.BOOL,
    wintypes.LPCWSTR,       # _In_opt_    lpApplicationName
    wintypes.LPWSTR,        # _Inout_opt_ lpCommandLine
    LPSECURITY_ATTRIBUTES,  # _In_opt_    lpProcessAttributes
    LPSECURITY_ATTRIBUTES,  # _In_opt_    lpThreadAttributes
    wintypes.BOOL,          # _In_        bInheritHandles
    wintypes.DWORD,         # _In_        dwCreationFlags
    wintypes.LPCWSTR,       # _In_opt_    lpEnvironment
    wintypes.LPCWSTR,       # _In_opt_    lpCurrentDirectory
    LPSTARTUPINFO,          # _In_        lpStartupInfo
    LPPROCESS_INFORMATION)  # _Out_       lpProcessInformation

# https://msdn.microsoft.com/en-us/library/ms682429
WIN(advapi32.CreateProcessAsUserW, wintypes.BOOL,
    wintypes.HANDLE,        # _In_opt_    hToken
    wintypes.LPCWSTR,       # _In_opt_    lpApplicationName
    wintypes.LPWSTR,        # _Inout_opt_ lpCommandLine
    LPSECURITY_ATTRIBUTES,  # _In_opt_    lpProcessAttributes
    LPSECURITY_ATTRIBUTES,  # _In_opt_    lpThreadAttributes
    wintypes.BOOL,          # _In_        bInheritHandles
    wintypes.DWORD,         # _In_        dwCreationFlags
    wintypes.LPCWSTR,       # _In_opt_    lpEnvironment
    wintypes.LPCWSTR,       # _In_opt_    lpCurrentDirectory
    LPSTARTUPINFO,          # _In_        lpStartupInfo
    LPPROCESS_INFORMATION)  # _Out_       lpProcessInformation

# https://msdn.microsoft.com/en-us/library/ms682434
WIN(advapi32.CreateProcessWithTokenW, wintypes.BOOL,
    wintypes.HANDLE,        # _In_        hToken
    wintypes.DWORD,         # _In_        dwLogonFlags
    wintypes.LPCWSTR,       # _In_opt_    lpApplicationName
    wintypes.LPWSTR,        # _Inout_opt_ lpCommandLine
    wintypes.DWORD,         # _In_        dwCreationFlags
    wintypes.LPCWSTR,       # _In_opt_    lpEnvironment
    wintypes.LPCWSTR,       # _In_opt_    lpCurrentDirectory
    LPSTARTUPINFO,          # _In_        lpStartupInfo
    LPPROCESS_INFORMATION)  # _Out_       lpProcessInformation

# https://msdn.microsoft.com/en-us/library/ms682431
WIN(advapi32.CreateProcessWithLogonW, wintypes.BOOL,
    wintypes.LPCWSTR,       # _In_        lpUsername
    wintypes.LPCWSTR,       # _In_opt_    lpDomain
    wintypes.LPCWSTR,       # _In_        lpPassword
    wintypes.DWORD,         # _In_        dwLogonFlags
    wintypes.LPCWSTR,       # _In_opt_    lpApplicationName
    wintypes.LPWSTR,        # _Inout_opt_ lpCommandLine
    wintypes.DWORD,         # _In_        dwCreationFlags
    wintypes.LPCWSTR,       # _In_opt_    lpEnvironment
    wintypes.LPCWSTR,       # _In_opt_    lpCurrentDirectory
    LPSTARTUPINFO,          # _In_        lpStartupInfo
    LPPROCESS_INFORMATION)  # _Out_       lpProcessInformation


CREATION_TYPE_NORMAL = 0
CREATION_TYPE_LOGON  = 1
CREATION_TYPE_TOKEN  = 2
CREATION_TYPE_USER   = 3

class CREATIONINFO(object):
    __slots__ = ('dwCreationType',
        'lpApplicationName', 'lpCommandLine', 'bUseShell',
        'lpProcessAttributes', 'lpThreadAttributes', 'bInheritHandles',
        'dwCreationFlags', 'lpEnvironment', 'lpCurrentDirectory',
        'hToken', 'lpUsername', 'lpDomain', 'lpPassword', 'dwLogonFlags')

    def __init__(self, dwCreationType=CREATION_TYPE_NORMAL,
                 lpApplicationName=None, lpCommandLine=None, bUseShell=False,
                 lpProcessAttributes=None, lpThreadAttributes=None,
                 bInheritHandles=False, dwCreationFlags=0, lpEnvironment=None,
                 lpCurrentDirectory=None, hToken=None, dwLogonFlags=0,
                 lpUsername=None, lpDomain=None, lpPassword=None):
        self.dwCreationType = dwCreationType
        self.lpApplicationName = lpApplicationName
        self.lpCommandLine = lpCommandLine
        self.bUseShell = bUseShell
        self.lpProcessAttributes = lpProcessAttributes
        self.lpThreadAttributes = lpThreadAttributes
        self.bInheritHandles = bInheritHandles
        self.dwCreationFlags = dwCreationFlags
        self.lpEnvironment = lpEnvironment
        self.lpCurrentDirectory = lpCurrentDirectory
        self.hToken = hToken
        self.lpUsername = lpUsername
        self.lpDomain = lpDomain
        self.lpPassword = lpPassword
        self.dwLogonFlags = dwLogonFlags

def create_environment(environ):
    if environ is not None:
        items = ['%s=%s' % (k, environ[k]) for k in sorted(environ)]
        buf = '\x00'.join(items)
        length = len(buf) + 2 if buf else 1
        return ctypes.create_unicode_buffer(buf, length)

def create_process(commandline=None, creationinfo=None, startupinfo=None):
    if creationinfo is None:
        creationinfo = CREATIONINFO()

    if startupinfo is None:
        startupinfo = STARTUPINFO()
    elif isinstance(startupinfo, subprocess.STARTUPINFO):
        startupinfo = STARTUPINFO(dwFlags=startupinfo.dwFlags,
                        hStdInput=startupinfo.hStdInput,
                        hStdOutput=startupinfo.hStdOutput,
                        hStdError=startupinfo.hStdError,
                        wShowWindow=startupinfo.wShowWindow)

    si, ci, pi = startupinfo, creationinfo, PROCESS_INFORMATION()

    if commandline is None:
        commandline = ci.lpCommandLine

    if commandline is not None:
        if ci.bUseShell:
            si.dwFlags |= STARTF_USESHOWWINDOW
            si.wShowWindow = SW_HIDE
            comspec = os.environ.get("ComSpec", os.path.join(
                        os.environ["SystemRoot"], "System32", "cmd.exe"))
            commandline = '"{}" /c "{}"'.format(comspec, commandline)
        commandline = ctypes.create_unicode_buffer(commandline)

    dwCreationFlags = ci.dwCreationFlags | CREATE_UNICODE_ENVIRONMENT
    lpEnvironment = create_environment(ci.lpEnvironment)

    if (dwCreationFlags & DETACHED_PROCESS and
       ((dwCreationFlags & CREATE_NEW_CONSOLE) or
        (ci.dwCreationType == CREATION_TYPE_LOGON) or
        (ci.dwCreationType == CREATION_TYPE_TOKEN))):
        raise RuntimeError('DETACHED_PROCESS is incompatible with '
                           'CREATE_NEW_CONSOLE, which is implied for '
                           'the logon and token creation types')

    if ci.dwCreationType == CREATION_TYPE_NORMAL:

        kernel32.CreateProcessW(
            ci.lpApplicationName, commandline,
            ci.lpProcessAttributes, ci.lpThreadAttributes, ci.bInheritHandles,
            dwCreationFlags, lpEnvironment, ci.lpCurrentDirectory,
            ctypes.byref(si), ctypes.byref(pi))

    elif ci.dwCreationType == CREATION_TYPE_LOGON:

        advapi32.CreateProcessWithLogonW(
            ci.lpUsername, ci.lpDomain, ci.lpPassword, ci.dwLogonFlags,
            ci.lpApplicationName, commandline,
            dwCreationFlags, lpEnvironment, ci.lpCurrentDirectory,
            ctypes.byref(si), ctypes.byref(pi))

    elif ci.dwCreationType == CREATION_TYPE_TOKEN:

        advapi32.CreateProcessWithTokenW(
            ci.hToken, ci.dwLogonFlags,
            ci.lpApplicationName, commandline,
            dwCreationFlags, lpEnvironment, ci.lpCurrentDirectory,
            ctypes.byref(si), ctypes.byref(pi))

    elif ci.dwCreationType == CREATION_TYPE_USER:

        advapi32.CreateProcessAsUserW(
            ci.hToken,
            ci.lpApplicationName, commandline,
            ci.lpProcessAttributes, ci.lpThreadAttributes, ci.bInheritHandles,
            dwCreationFlags, lpEnvironment, ci.lpCurrentDirectory,
            ctypes.byref(si), ctypes.byref(pi))

    else:
        raise ValueError('invalid process creation type')

    return pi

class Popen(subprocess.Popen):
    def __init__(self, *args, **kwds):
        ci = self._creationinfo = kwds.pop('creationinfo', CREATIONINFO())
        if kwds.pop('suspended', False):
            ci.dwCreationFlags |= CREATE_SUSPENDED
        self._child_started = False
        super(Popen, self).__init__(*args, **kwds)

    if sys.version_info[0] == 2:

        def _execute_child(self, args, executable, preexec_fn, close_fds,
                           cwd, env, universal_newlines, startupinfo,
                           creationflags, shell, to_close, p2cread, p2cwrite,
                           c2pread, c2pwrite, errread, errwrite):
            """Execute program (MS Windows version)"""
            commandline = (args if isinstance(args, types.StringTypes) else
                           subprocess.list2cmdline(args))
            self._common_execute_child(executable, commandline, shell, 
                    close_fds, creationflags, env, cwd,
                    startupinfo, p2cread, c2pwrite, errwrite, to_close)
    else:

        def _execute_child(self, args, executable, preexec_fn, close_fds,
                           pass_fds, cwd, env, startupinfo, creationflags,
                           shell, p2cread, p2cwrite, c2pread, c2pwrite, errread,
                           errwrite, restore_signals, start_new_session):
            """Execute program (MS Windows version)"""
            assert not pass_fds, "pass_fds not supported on Windows."
            commandline = (args if isinstance(args, str) else
                           subprocess.list2cmdline(args))
            self._common_execute_child(executable, commandline, shell, 
                    close_fds, creationflags, env, cwd,
                    startupinfo, p2cread, c2pwrite, errwrite)

    def _common_execute_child(self, executable, commandline, shell,
                              close_fds, creationflags, env, cwd,
                              startupinfo, p2cread, c2pwrite, errwrite,
                              to_close=()):

        ci = self._creationinfo
        if executable is not None:
            ci.lpApplicationName = executable
        if commandline:
            ci.lpCommandLine = commandline
        if shell:
            ci.bUseShell = shell
        if not close_fds:
            ci.bInheritHandles = int(not close_fds)
        if creationflags:
            ci.dwCreationFlags |= creationflags
        if env is not None:
            ci.lpEnvironment = env
        if cwd is not None:
            ci.lpCurrentDirectory = cwd

        if startupinfo is None:
            startupinfo = STARTUPINFO()
        si = self._startupinfo = startupinfo

        default = None if sys.version_info[0] == 2 else -1
        if default not in (p2cread, c2pwrite, errwrite):
            si.dwFlags |= STARTF_USESTDHANDLES
            si.hStdInput  = int( p2cread)
            si.hStdOutput = int(c2pwrite)
            si.hStdError  = int(errwrite)

        try:
            pi = create_process(creationinfo=ci, startupinfo=si)
        finally:
            if sys.version_info[0] == 2:
                if p2cread is not None:
                    p2cread.Close()
                    to_close.remove(p2cread)
                if c2pwrite is not None:
                    c2pwrite.Close()
                    to_close.remove(c2pwrite)
                if errwrite is not None:
                    errwrite.Close()
                    to_close.remove(errwrite)
            else:
                if p2cread != -1:
                    p2cread.Close()
                if c2pwrite != -1:
                    c2pwrite.Close()
                if errwrite != -1:
                    errwrite.Close()
                if hasattr(self, '_devnull'):
                    os.close(self._devnull)

        if not ci.dwCreationFlags & CREATE_SUSPENDED:
            self._child_started = True

        # Retain the process handle, but close the thread handle
        # if it's no longer needed.
        self._processinfo = pi
        self._handle = pi.hProcess.Detach()
        self.pid = pi.dwProcessId
        if self._child_started:
            pi.hThread.Close()

    def start(self):
        if self._child_started:
            raise RuntimeError("processes can only be started once")
        hThread = self._processinfo.hThread
        prev_count = kernel32.ResumeThread(hThread)
        if prev_count > 1:
            for i in range(1, prev_count):
                if kernel32.ResumeThread(hThread) <= 1:
                    break
            else:
                raise RuntimeError('cannot start the main thread')
        # The thread's previous suspend count was 0 or 1, 
        # so it should be running now.
        self._child_started = True
        hThread.Close()

    def __del__(self):
        if not self._child_started:
            try:
                if hasattr(self, '_processinfo'):
                    self._processinfo.hThread.Close()
            finally:
                if hasattr(self, '_handle'):
                    self.terminate()
        super(Popen, self).__del__()

....

cmd = "rundll32.exe user32.dll,LockWorkStation" #lock_file
ci = CREATIONINFO(CREATION_TYPE_LOGON,
                 lpUsername=user,
                 lpPassword=pword)

p = Popen(cmd, suspended=True, creationinfo=ci,
          stdout=PIPE, universal_newlines=True)
p.start()

失败并出现相同的错误。

然后我们到达实际可行的最终解决方案,即WTSGetActiveConsoleSessionId在调用之前WTSGetActiveConsoleSessionId使用use 。恕我直言,最重要的部分是这个工作所必需的。前两种方法允许我们不使用预定义的用户/通行证。 CreateEnvironmentBlockCreateProcessAsUserCreateEnvironmentBlock

有用的链接:

@EugeneMayevski'Callback 提供了两个讨论这个问题的链接:1.calling
- createprocessasuser-from-service
2.calling -createprocessasuser-from-a-user-process-launched-from-a-service

重要的提示 :

在调试模式下,使用我提供的代码段时,您可能会遇到 1314 错误,WTSGetActiveConsoleSessionId并且实时服务不会遇到此错误并且运行良好。


推荐阅读