c# - Windows 2004 更新后无法以其他用户身份运行 EXCEL.EXE

标签 c# windows powershell office365 impersonation

我需要用不同于当前用户的用户运行 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/

相关文章:

java - 从 Windows 批处理文件中自行删除 jar

ruby - Typhoeus Windows 安装

PowerShell 输出文件

c# - DateTime 算法建议

C# PrincipalContext 错误 "Server names cannot contain a space character"

linux - Windows 与 Linux 的内存使用情况

powershell - 如何更改我键入的字符串值的颜色?

powershell - 检查服务是否正在运行或停止并相应输出

c# - 在 VS 08 和 C# 中获得生产力的最快方法

c# - 具有最大大小且无重复的先进先出集合?