我需要用不同于当前用户的用户运行 EXCEL.EXE;过去我这样做没有任何问题,但在将系统更新到 Windows 10 专业版 2004 (19041.508) 后,此方法不再有效。
我正在使用以下类(class)(感谢 Impersonate user in Windows Service ):
public class CreateProcess
{
#region Constants
const UInt32 INFINITE = 0xFFFFFFFF;
const UInt32 WAIT_FAILED = 0xFFFFFFFF;
#endregion
#region ENUMS
[Flags]
public enum LogonType
{
LOGON32_LOGON_INTERACTIVE = 2,
LOGON32_LOGON_NETWORK = 3,
LOGON32_LOGON_BATCH = 4,
LOGON32_LOGON_SERVICE = 5,
LOGON32_LOGON_UNLOCK = 7,
LOGON32_LOGON_NETWORK_CLEARTEXT = 8,
LOGON32_LOGON_NEW_CREDENTIALS = 9
}
[Flags]
public enum LogonProvider
{
LOGON32_PROVIDER_DEFAULT = 0,
LOGON32_PROVIDER_WINNT35,
LOGON32_PROVIDER_WINNT40,
LOGON32_PROVIDER_WINNT50
}
#endregion
#region Structs
[StructLayout(LayoutKind.Sequential)]
public 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;
}
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public Int32 dwProcessId;
public Int32 dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public unsafe byte* lpSecurityDescriptor;
public int bInheritHandle;
}
public enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation
}
public enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous,
SecurityIdentification,
SecurityImpersonation,
SecurityDelegation
}
#endregion
#region FUNCTIONS (P/INVOKE)
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool RevertToSelf();
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int DuplicateToken(IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern Boolean LogonUser
(
String UserName,
String Domain,
String Password,
LogonType dwLogonType,
LogonProvider dwLogonProvider,
out IntPtr phToken
);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Boolean CreateProcessAsUser
(
IntPtr hToken,
String lpApplicationName,
String lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
Boolean bInheritHandles,
Int32 dwCreationFlags,
IntPtr lpEnvironment,
String lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation
);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern UInt32 WaitForSingleObject
(
IntPtr hHandle,
UInt32 dwMilliseconds
);
[DllImport("kernel32", SetLastError = true)]
public static extern Boolean CloseHandle(IntPtr handle);
#endregion
#region Functions
public static int LaunchCommand(string command, string domain, string account, string password)
{
int ProcessId = -1;
PROCESS_INFORMATION processInfo = new PROCESS_INFORMATION();
STARTUPINFO startInfo = new STARTUPINFO();
Boolean bResult = false;
UInt32 uiResultWait = WAIT_FAILED;
var token = ValidateParametersAndGetFirstLoginToken(domain, account, password);
var duplicateToken = IntPtr.Zero;
try
{
startInfo.cb = Marshal.SizeOf(startInfo);
startInfo.lpDesktop = "winsta0\\default";
bResult = CreateProcessAsUser(
token,
null,
command,
IntPtr.Zero,
IntPtr.Zero,
false,
0,
IntPtr.Zero,
null,
ref startInfo,
out processInfo
);
if (!bResult) { throw new Exception("CreateProcessAsUser error #" + Marshal.GetLastWin32Error()); }
// Wait for process to end
uiResultWait = WaitForSingleObject(processInfo.hProcess, INFINITE);
ProcessId = processInfo.dwProcessId;
if (uiResultWait == WAIT_FAILED) { throw new Exception("WaitForSingleObject error #" + Marshal.GetLastWin32Error()); }
}
finally
{
if (token != IntPtr.Zero)
CloseHandle(token);
if (duplicateToken != IntPtr.Zero)
CloseHandle(duplicateToken);
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
}
return ProcessId;
}
private static IntPtr ValidateParametersAndGetFirstLoginToken(string domain, string username, string password)
{
if (!RevertToSelf())
{
Console.WriteLine("RevertToSelf call to remove any prior impersonations failed");
throw new Exception("RevertToSelf call to remove any prior impersonations failed");
}
IntPtr token;
var result = LogonUser(username,
domain,
password,
LogonType.LOGON32_LOGON_INTERACTIVE,
LogonProvider.LOGON32_PROVIDER_DEFAULT,
out token);
if (!result)
{
var errorCode = Marshal.GetLastWin32Error();
Console.WriteLine(string.Format("Could not impersonate the elevated user. LogonUser: {2}\\{1} returned error code: {0}.", errorCode, username, domain));
throw new Exception("Logon for user " + username + " failed.");
}
return token;
}
#endregion
}
这样:
var otherUserInSystemName = "probanduela";
var otherUserInSystemPassword = "chapuza";
var regularWin32AppPath = @"C:\Program Files (x86)\WinMerge\WinMergeU.exe";
var officeWin32AppPath = @"C:\Program Files (x86)\Microsoft Office\root\Office16\EXCEL.EXE";
var ProcessId = CreateProcess.LaunchCommand(officeWin32AppPath, Environment.MachineName, otherUserInSystemName, otherUserInSystemPassword);
我总是收到错误:
1312: A specified logon session does not exist. It may already have been terminated.
也尝试使用 CreateProcessWithLogonW,结果相同。
如果我尝试通过 PowerShell 或“runas”运行它,会发生类似的问题。
此方法在升级到 Windows 10 2004 版本之前有效,我已在不同计算机上重现此方法。
问题是什么?我怎样才能实现我想做的事情?
-编辑-
刚刚发现使用 Excel v2002 build 12527.21104 可以正常工作;但使用 Excel v2008 build 13127.20408 失败。
显然是 Windows 10 v2004 + Office 2019 v2008 的组合导致了这个问题。
-编辑2-
如果我复制 EXCEL.EXE 并给它另一个名字,比如“EXCEL_COPY.EXE”,它就可以工作 :S
最佳答案
我向 Microsoft 报告了该问题,在提升了几个级别之后,他们已经着手解决并修复了该问题。它固定在 Windows 10 Build 19042.844
关于c# - Windows 2004 更新后无法以其他用户身份运行 EXCEL.EXE,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64052694/