问题描述
我需要以不同于当前用户的用户身份运行EXCEL.EXE;过去我这样做没有任何问题,但是在将系统更新为Windows 10 Pro 2004版(19041.508)之后,此方法不再起作用。
我正在使用以下课程(感谢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",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",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,false,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内部版本13127.20408失败。
Windows 10 v2004 + Office 2019 v2008的组合显然是导致此问题的原因。
-Edit2-
如果我复制EXCEL.EXE并给它起另一个名字,例如“ EXCEL_COPY.EXE”,它就会起作用:S
解决方法
我向微软报告了这个问题,在提高了几个级别后,他们已经解决并修复了。已于 Windows 10 Build 19042.844
修复