我有一个计划任务,它运行一个批处理文件来执行一些操作。我希望该批处理文件启动另一个在用户空间中运行的批处理文件;即 %username% == 当前登录的用户,具有用户权限等。假设计划任务以 SYSTEM 权限运行,我如何在当前用户下运行命令?
对于 Windows XP 和 Windows 7 我都需要这个。
答案1
注(2013): 当这个答案发布时,我还不知道
psexec
。使用它可能会更容易。
自从引入终端服务以来,“当前用户”可以是复数。甚至 XP 也支持快速用户切换。
您能得到最接近的结果就是“用户连接到控制台会话”。为此,请使用复制代码+WTS查询用户令牌()+创建环境块()+创建进程作为用户()。
我用 C# 编写了一个程序(见下面的附件)——编译,以批处理文件的完整路径作为参数运行。
是的,这需要 .NET Runtime,但您的系统可能已经安装了它。Runtime 的编译器部分也是如此:(%SystemRoot%\Microsoft.NET\Framework64\v3.5\csc.exe
任何以 开头的版本v2.*
都可以使用)。
注意:WTSQueryUserToken() 要求程序以如下方式运行本地系统。 (根据文档,权限是不够的,但我还没有检查过。
依恋:RunConsole.cs
// (c) 2011 Mantas Mikulėnas <[email protected]>
// Released under the MIT license <https://spdx.org/licenses/MIT>
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
class RunConsole {
[DllImport("kernel32.dll")]
static extern uint WTSGetActiveConsoleSessionId();
[DllImport("wtsapi32.dll", SetLastError=true)]
static extern bool WTSQueryUserToken(UInt32 sessionId, out IntPtr Token);
[DllImport("userenv.dll", SetLastError=true)]
static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment, IntPtr hToken, bool bInherit);
[DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
static extern bool CreateProcessAsUser(
IntPtr hToken,
string lpApplicationName,
string lpCommandLine,
ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct STARTUPINFO
{
public Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[Flags]
enum CreateProcessFlags : uint
{
CREATE_BREAKAWAY_FROM_JOB = 0x01000000,
CREATE_DEFAULT_ERROR_MODE = 0x04000000,
CREATE_NEW_CONSOLE = 0x00000010,
CREATE_NEW_PROCESS_GROUP = 0x00000200,
CREATE_NO_WINDOW = 0x08000000,
CREATE_PROTECTED_PROCESS = 0x00040000,
CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000,
CREATE_SEPARATE_WOW_VDM = 0x00000800,
CREATE_SHARED_WOW_VDM = 0x00001000,
CREATE_SUSPENDED = 0x00000004,
CREATE_UNICODE_ENVIRONMENT = 0x00000400,
DEBUG_ONLY_THIS_PROCESS = 0x00000002,
DEBUG_PROCESS = 0x00000001,
DETACHED_PROCESS = 0x00000008,
EXTENDED_STARTUPINFO_PRESENT = 0x00080000,
INHERIT_PARENT_AFFINITY = 0x00010000,
}
static int Main(string[] argv)
{
string cwd = Environment.CurrentDirectory;
string appName = argv[0];
if (appName.Length == 0)
{
throw new InvalidOperationException("Application not specified.");
}
// TODO: Deal with quoted args, etc.
// For now, good enough to start a .cmd script.
string commandLine = String.Join(" ", argv);
uint sessionId = WTSGetActiveConsoleSessionId();
if (sessionId == 0xFFFFFFFF)
{
throw new InvalidOperationException("No session attached to the physical console.");
}
IntPtr hToken;
if (!WTSQueryUserToken(sessionId, out hToken))
{
throw new Win32Exception();
}
IntPtr lpEnvironment;
if (!CreateEnvironmentBlock(out lpEnvironment, hToken, false))
{
throw new Win32Exception();
}
SECURITY_ATTRIBUTES saProcessAttributes = new SECURITY_ATTRIBUTES();
SECURITY_ATTRIBUTES saThreadAttributes = new SECURITY_ATTRIBUTES();
STARTUPINFO startupInfo = new STARTUPINFO();
PROCESS_INFORMATION processInfo;
CreateProcessFlags flags = 0;
//flags |= CreateProcessFlags.CREATE_NEW_CONSOLE;
flags |= CreateProcessFlags.CREATE_UNICODE_ENVIRONMENT;
if (!CreateProcessAsUser(hToken, appName, commandLine,
ref saProcessAttributes, ref saThreadAttributes, false, (uint)flags,
lpEnvironment, cwd, ref startupInfo, out processInfo))
{
throw new Win32Exception();
}
Console.WriteLine("pid = {0}", processInfo.dwProcessId);
return 0;
}
}
答案2
在“运行任务时,使用以下用户帐户:”下,您可以将其设置为“BUILTIN\Users”组,它将以当前登录用户的身份运行该任务。
答案3
我认为您无法通过任务计划程序工具将其设置为动态使用当前登录的用户,但您可以在任务本身中指定除系统之外的用户帐户。以下两个选项都要求您拥有用户名的密码。
Windows 7:在任务的属性窗口中,安全选项中有一个名为“更改用户或组”的按钮。
Windows XP:在任务的属性窗口中,您可以在“以...身份运行”字段中输入用户名。
最后一个选项可能是创建一个启动脚本,该脚本将在用户登录时创建任务,并创建一个配套的注销脚本,以便在用户注销时删除该任务。命令是“Schtasks”,在 XP 和 7 中几乎相同。该命令的文档可从 MS 的以下站点获取: http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/schtasks.mspx?mfr=true