CreateProcessAsUser不会在非交互会话中打开我的Winform

时间:2019-07-04 07:22:41

标签: winforms winapi windows-services interactive

我正在尝试从Windows服务中启动Winforms应用程序进程。 到目前为止,我已经尝试过使用TopShelf并生成Winforms消息框。 由于某些原因,我不得不停止使用TopShelf,现在我在Winforms应用程序中不断收到此错误。

  

在应用程序未运行时显示模式对话框或表单   在UserInteractive模式下不是有效操作。指定   ServiceNotification或DefaultDesktopOnly样式以显示   服务应用程序发出的通知。

PS 我也提出了类似的问题here,一个答复是使用PInvoke。自TopShelf刚开始工作以来,我就没有这么做,但我不知道该怎么做。 (如果重复,我将删除此帖子并接受上一篇-但我不确定现在是否是这种情况。)

我尝试使用Win32 API方法CreateProcessAsUser,并且当它尝试显示表单对话框时我可以打开该进程时,我仍然遇到相同的错误。

Win32方法

public static void Do() {
            IntPtr impToken = WindowsIdentity.GetCurrent().Token;
            IntPtr clonedToken = IntPtr.Zero;
            Win32.PROCESS_INFORMATION pi = new Win32.PROCESS_INFORMATION();
            try {
                Win32.SECURITY_ATTRIBUTES sa = new Win32.SECURITY_ATTRIBUTES();
                sa.Length = Marshal.SizeOf(sa);
                bool result = Win32.DuplicateTokenEx(impToken,
                    Win32.GENERIC_ALL_ACCESS,
                    ref sa,
                    (int)Win32.SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
                    (int)Win32.TOKEN_TYPE.TokenPrimary,
                    ref clonedToken);
                if (!result) {
                    return;
                }
                Win32.STARTUPINFO info = new Win32.STARTUPINFO();
                info.cb = Marshal.SizeOf(info);
                info.lpDesktop = string.Empty;
                result = Win32.CreateProcessAsUser(clonedToken, Constants.USER.PROCESS_FILEPATH, string.Empty,
                    ref sa, ref sa, false, 0, IntPtr.Zero, @"C:\", ref info, ref pi);
                int error = Marshal.GetLastWin32Error();
                string message = string.Format($"Create Process As User Error:{0}", error);

            } catch (Exception) {

                if (pi.hProcess != IntPtr.Zero) {
                    Win32.CloseHandle(pi.hProcess);
                }
                if (pi.hThread != IntPtr.Zero) {
                    Win32.CloseHandle(pi.hThread);
                }
                if (clonedToken != IntPtr.Zero) {
                    Win32.CloseHandle(clonedToken);
                }
            }
        }

Win32绑定

class Win32 {

        [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 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 Int32 Length;
            public IntPtr lpSecurityDescriptor;
            public bool bInheritHandle;
        }

        public enum SECURITY_IMPERSONATION_LEVEL {
            SecurityAnonymous,
            SecurityIdentification,
            SecurityImpersonation,
            SecurityDelegation
        }

        public enum TOKEN_TYPE {
            TokenPrimary = 1,
            TokenImpersonation
        }

        public const int GENERIC_ALL_ACCESS = 0x10000000;

        [
           DllImport("kernel32.dll",
              EntryPoint = "CloseHandle", SetLastError = true,
              CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)
        ]
        public static extern bool CloseHandle(IntPtr handle);

        [
           DllImport("advapi32.dll",
              EntryPoint = "CreateProcessAsUser", SetLastError = true,
              CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)
        ]
        public static extern bool
           CreateProcessAsUser(IntPtr hToken, string lpApplicationName, string lpCommandLine,
                               ref SECURITY_ATTRIBUTES lpProcessAttributes, ref SECURITY_ATTRIBUTES lpThreadAttributes,
                               bool bInheritHandle, Int32 dwCreationFlags, IntPtr lpEnvrionment,
                               string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo,
                               ref PROCESS_INFORMATION lpProcessInformation);

        [
           DllImport("advapi32.dll",
              EntryPoint = "DuplicateTokenEx")
        ]
        public static extern bool
           DuplicateTokenEx(IntPtr hExistingToken, Int32 dwDesiredAccess,
                            ref SECURITY_ATTRIBUTES lpThreadAttributes,
                            Int32 ImpersonationLevel, Int32 dwTokenType,
                            ref IntPtr phNewToken);


    }

Winform

[STAThread]
 static void Main(){
  try {
       var form = new SomeForm();
       form.ShowDialog();

      } catch (Exception ex) {

      }
 }

因此我可以从非交互模式创建流程,但仍然无法打开对话框。我认为使用CreateProcessAsUser可以打开Winform。

更新我在阅读CreateProcessAsUser的{​​{3}}时发现了以下有趣的信息:

  

默认情况下,CreateProcessAsUser在   非交互式窗口站,其中的桌面不可见,并且   无法接收用户输入。启用与新用户的交互   过程中,必须指定默认交互窗口的名称   工作站和桌面上的lpDesktop成员中的“ winsta0 \ default”   STARTUPINFO结构。另外,打电话之前   CreateProcessAsUser,您必须更改自由访问控制   默认交互式窗口站和   默认桌面。 Window Station和桌面的DACL必须   向用户授予访问权限或由代表的登录会话   hToken参数。

附言:任何人都可以阐明我如何进行这项工作吗?我已经阅读了所有有关使用令牌复制和使用win32 api的文章。

更新 我设法绕过该错误。似乎问题出在STARTUPINFO.lpDesktop字段。它设置为String.Empty。将其设置为默认值:@winsta0\default之后,就没有了更多错误。

现在没有错误了,form.ShowDialog不会抛出任何错误,但是仍然不可见。我还能做什么?

0 个答案:

没有答案