如何使用C#从Windows服务运行EXE
程序?
这是我的代码:
System.Diagnostics.Process.Start(@"E:\PROJECT XL\INI SQLLOADER\ConsoleApplication2\ConsoleApplication2\ConsoleApplication2\bin\Debug\ConsoleApplication2.exe");
当我运行此服务时,应用程序无法启动。 我的代码出了什么问题?
这将永远不会工作,至少不在Windows Vista或更高版本下。关键问题是您尝试在Windows服务中执行此操作,而不是标准Windows应用程序。您显示的代码将在Windows窗体,WPF或控制台应用程序中完美运行,但在Windows服务中根本不起作用。
Windows服务无法启动其他应用程序,因为它们未在任何特定用户的上下文中运行。与常规Windows应用程序不同,services are now run in an isolated session禁止与用户或桌面交互。这使得应用程序无法运行。
有关这些相关问题的答案中提供了更多信息:
正如您现在可能已经想到的那样,解决您问题的最佳方法是创建标准Windows应用程序而不是服务。这些设计旨在由特定用户运行,并与该用户的桌面相关联。这样,您可以使用已经显示的代码随时运行其他应用程序。
假设您的控制台应用程序不需要任何类型的接口或输出,另一种可能的解决方案是指示进程不要创建窗口。这将阻止Windows阻止您的进程创建,因为它将不再请求创建控制台窗口。您可以在this answer中找到相关问题的相关代码。
我试过这篇文章Code Project,它对我来说很好。我也使用过代码。文章在截图中的解释非常好。
我正在为这个场景添加必要的解释
您刚刚启动计算机并即将登录。登录时,系统会为您分配唯一的会话ID。在Windows Vista中,操作系统会为第一个登录计算机的用户分配一个会话ID为1。下一个登录用户将被分配一个会话ID为2.依此类推。您可以从“任务管理器”的“用户”选项卡中查看分配给每个登录用户的会话ID。
但是您的Windows服务会议ID为0。此会话与其他会话隔离。这最终会阻止Windows服务调用在用户会话(如1或2)下运行的应用程序。
要从Windows服务调用应用程序,您需要从winlogon.exe复制控件,该控件充当当前登录用户,如下面的屏幕截图所示。
重要代码
// obtain the process id of the winlogon process that
// is running within the currently active session
Process[] processes = Process.GetProcessesByName("winlogon");
foreach (Process p in processes)
{
if ((uint)p.SessionId == dwSessionId)
{
winlogonPid = (uint)p.Id;
}
}
// obtain a handle to the winlogon process
hProcess = OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid);
// obtain a handle to the access token of the winlogon process
if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE, ref hPToken))
{
CloseHandle(hProcess);
return false;
}
// Security attibute structure used in DuplicateTokenEx and CreateProcessAsUser
// I would prefer to not have to use a security attribute variable and to just
// simply pass null and inherit (by default) the security attributes
// of the existing token. However, in C# structures are value types and therefore
// cannot be assigned the null value.
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
sa.Length = Marshal.SizeOf(sa);
// copy the access token of the winlogon process;
// the newly created token will be a primary token
if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa,
(int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
(int)TOKEN_TYPE.TokenPrimary, ref hUserTokenDup))
{
CloseHandle(hProcess);
CloseHandle(hPToken);
return false;
}
STARTUPINFO si = new STARTUPINFO();
si.cb = (int)Marshal.SizeOf(si);
// interactive window station parameter; basically this indicates
// that the process created can display a GUI on the desktop
si.lpDesktop = @"winsta0\default";
// flags that specify the priority and creation method of the process
int dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
// create a new process in the current User's logon session
bool result = CreateProcessAsUser(hUserTokenDup, // client's access token
null, // file to execute
applicationName, // command line
ref sa, // pointer to process SECURITY_ATTRIBUTES
ref sa, // pointer to thread SECURITY_ATTRIBUTES
false, // handles are not inheritable
dwCreationFlags, // creation flags
IntPtr.Zero, // pointer to new environment block
null, // name of current directory
ref si, // pointer to STARTUPINFO structure
out procInfo // receives information about new process
);
你可以使用Windows任务调度程序为此目的,有许多像TaskScheduler这样的库可以帮助你。
例如,我们想要调度一个将在五秒后执行的任务:
using (var ts = new TaskService())
{
var t = ts.Execute("notepad.exe")
.Once()
.Starting(DateTime.Now.AddSeconds(5))
.AsTask("myTask");
}
notepad.exe将在五秒后执行。
有关详细信息和更多信息,请访问wiki
如果你知道你需要那个程序集中的哪个类和方法,你可以自己调用它:
Assembly assembly = Assembly.LoadFrom("yourApp.exe");
Type[] types = assembly.GetTypes();
foreach (Type t in types)
{
if (t.Name == "YourClass")
{
MethodInfo method = t.GetMethod("YourMethod",
BindingFlags.Public | BindingFlags.Instance);
if (method != null)
{
ParameterInfo[] parameters = method.GetParameters();
object classInstance = Activator.CreateInstance(t, null);
var result = method.Invoke(classInstance, parameters.Length == 0 ? null : parameters);
break;
}
}
}
您可以在Windows XP中很好地从Windows服务执行.exe。我过去自己做过。
您需要确保已在Windows服务属性中选中了“允许与桌面交互”选项。如果没有这样做,它将不会执行。
我需要检查Windows 7或Vista,因为这些版本需要额外的安全权限,因此可能会出错,但我确信它可以直接或间接实现。对于XP,我确信我自己也做过。
首先,我们将创建一个在System帐户下运行的Windows服务。此服务将负责在当前活动的用户会话中生成交互式进程。这个新创建的流程将显示一个用户界面,并以完全管理员权限运行。当第一个用户登录到计算机时,此服务将启动并将在Session0中运行;但是,此服务生成的进程将在当前登录用户的桌面上运行。我们将此服务称为LoaderService。
接下来,winlogon.exe进程负责管理用户登录和注销过程。我们知道登录到计算机的每个用户都将拥有唯一的会话ID和与其会话关联的相应winlogon.exe进程。现在,我们在上面提到过,LoaderService在System帐户下运行。我们还确认计算机上的每个winlogon.exe进程都在System帐户下运行。因为系统帐户是LoaderService和winlogon.exe进程的所有者,所以我们的LoaderService可以复制winlogon.exe进程的访问令牌(和会话ID),然后调用Win32 API函数CreateProcessAsUser来启动进程到当前活动的登录用户会话。由于位于复制的winlogon.exe进程的访问令牌中的会话ID大于0,因此我们可以使用该令牌启动交互式进程。
试试这个吧。 Subverting Vista UAC in Both 32 and 64 bit Architectures
我想你正在将.exe复制到不同的位置。这可能是我猜的问题。复制exe时,您不会复制其依赖项。
因此,您可以做的是,将所有依赖的dll放在GAC中,以便任何.net exe都可以访问它
否则,请勿将exe复制到新位置。只需创建一个环境变量并在c#中调用exe。由于路径是在环境变量中定义的,因此可以通过c#程序访问exe。
更新:
以前我在我的c#.net 3.5项目中有一些相同的问题,我试图从c#.net代码运行一个.exe文件,而exe只是另一个项目exe(我为我添加了几个支持dll)功能)和我在我的exe应用程序中使用的那些dll方法。最后,我通过将该应用程序创建为同一解决方案的单独项目来解决此问题,并将该项目输出添加到我的部署项目中。根据这种情况,我回答说,如果不是他想要的话,那我非常抱歉。
最热门的回答并没有错,但仍然与我发布的内容相反。我说它可以完全启动exe文件,你可以在任何用户的上下文中执行此操作。从逻辑上讲,您不能拥有任何用户界面或要求用户输入...
这是我的建议:
举例来说plink.exe。你甚至可以听输出:
var psi = new ProcessStartInfo()
{
FileName = "./Client/plink.exe", //path to your *.exe
Arguments = "-telnet -P 23 127.0.0.1 -l myUsername -raw", //arguments
RedirectStandardError = true,
RedirectStandardOutput = true,
RedirectStandardInput = true,
UseShellExecute = false,
CreateNoWindow = true //no window, you can't show it anyway
};
var p = Process.Start(psi);
我使用了默认的LocalSystem帐户,而不是本地服务。它工作正常,无需输入特定用户的登录信息。我甚至没有勾选“允许服务与桌面交互”复选框,如果您需要更高的权限,则可以使用该复选框。
最后我只是想说,最顶层的答案与我的答案完全相反,我们两个人都是正确的,这是多么有趣,这就是你如何解释这个问题:-D。如果你现在说,但你不能使用Windows服务项目类型 - 你可以,但我之前有这个和安装是粗略的,它可能是一种无意的黑客,直到我找到NSSM。
System.Diagnostics.Process.Start(“Exe Name”);