我正在尝试从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
不会抛出任何错误,但是仍然不可见。我还能做什么?