Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
.net 如何启动从windows服务到当前登录用户的进程';会议_.net_Windows Services - Fatal编程技术网

.net 如何启动从windows服务到当前登录用户的进程';会议

.net 如何启动从windows服务到当前登录用户的进程';会议,.net,windows-services,.net,Windows Services,我需要从Windows服务启动一个程序。该程序是一个用户界面应用程序。此外,该应用程序应该在特定的用户帐户下启动 问题是窗口服务在会话#0中运行,但登录的用户会话是1、2等 所以问题是:如何从窗口服务启动一个进程,使其在当前登录用户的会话中运行 我要强调的是,问题不是如何在特定帐户下启动进程(很明显是-process.start(newprocessstartinfo(“…”{UserName=…,Password=…}))。即使我将windows安装为在当前用户帐户下运行,该服务仍将在会话#0

我需要从Windows服务启动一个程序。该程序是一个用户界面应用程序。此外,该应用程序应该在特定的用户帐户下启动

问题是窗口服务在会话#0中运行,但登录的用户会话是1、2等

所以问题是:如何从窗口服务启动一个进程,使其在当前登录用户的会话中运行

我要强调的是,问题不是如何在特定帐户下启动进程(很明显是-process.start(newprocessstartinfo(“…”{UserName=…,Password=…}))。即使我将windows安装为在当前用户帐户下运行,该服务仍将在会话#0中运行。 设置“允许服务与桌面交互”没有帮助

我的windows服务基于.net

更新: 首先,.NET与此无关,它实际上是纯Win32的东西。 这是我正在做的。以下代码在我的windows服务中(C#通过P/Inkove使用win32函数,我跳过了导入签名,它们都在这里-):

代码转到“Notepad已由WatchdogService.Exitcode启动:”+Exitcode。出口代码是322225794。 而且没有任何新的记事本启动。
我哪里错了?

这样做是个坏主意。虽然可能并非完全不可能,但微软尽一切努力使这一点变得尽可能困难,因为它支持所谓的粉碎攻击。请看拉里·奥斯特曼:

这是一个坏主意的主要原因是,交互服务会导致一类称为“粉碎”攻击的威胁(因为我认为它们“粉碎了窗口”)

如果您搜索“粉碎攻击”,您可以看到这些安全威胁如何工作的一些细节。微软还发表了一篇文章,扩展了有关交互式服务的文档,Michael Howard为MSDN库撰写了一篇关于交互式服务的文章。最初,粉碎攻击是针对具有后台窗口消息泵的windows组件的(这些组件已经被修复了很久),但它们也被用来攻击弹出UI的第三方服务

这是一个坏主意的第二个原因是服务交互进程标志根本不能正常工作。服务UI在系统会话中弹出(通常为会话0)。另一方面,如果用户正在另一个会话中运行,则用户永远看不到UI。有两种主要的场景可以让用户连接到另一个会话中——终端服务和快速用户切换。TS并不常见,但在多人使用一台计算机的家庭场景中,FUS通常是启用的(例如,我们有4个人几乎一直在厨房的计算机上登录)

交互服务是一个坏主意的第三个原因是交互服务不能保证与Windows Vista一起使用:)作为进入Windows Vista的安全强化过程的一部分,交互用户登录到系统会话以外的会话-第一个交互用户在会话1而不是会话0中运行。这样做的效果是从根本上切断了粉碎攻击——用户应用程序无法与运行在服务中的高权限窗口交互

建议的解决方法是在用户的系统托盘中使用应用程序

如果您可以安全地忽略上述问题和警告,则可以按照此处给出的说明进行操作:


我不知道如何在.NET中执行此操作,但通常需要使用Win32 API
CreateProcessAsUser()
函数(或其.NET等效函数),指定所需的用户访问令牌和桌面名称。这是我在C++服务中使用的,它工作得很好。

< P> < /P> 这是一篇关于在Vista/7上的windows服务的交互式会话中启动新进程的非常有用的帖子

对于非本地系统服务,基本思想是:

  • 枚举进程以获取资源管理器的句柄

  • OpenProcessToken应该为您提供访问令牌。 注意:运行服务的帐户必须具有适当的权限才能调用此API并获取进程令牌

  • 一旦有了令牌,就可以使用此令牌调用CreateProcessAsUser。此令牌已具有正确的会话Id


我在这里找到了解决方案:


我认为这是一个很好的链接。

伯劳克回答的问题是,它不适用于通过RDP连接的用户。
这是我的解决方案,它在创建流程之前正确确定当前用户的会话。它已经在XP和7上进行了测试

您需要的所有内容都通过一个静态方法包装到一个.NET类中:

public static bool StartProcessAsCurrentUser(string appPath, string cmdLine, string workDir, bool visible)

下面是我如何实现它的。 它将尝试以当前登录用户(从服务)的身份启动进程。 这是基于几个来源放在一起的东西,工作

它实际上是纯WIN32/C++,因此可能对原始问题没有100%的帮助。 但我希望它能为其他人节省一些时间来寻找类似的东西

它需要Windows XP/2003(不适用于Windows 2000)。 您必须链接到Wtsapi32.lib

#define WINVER 0x0501
#define _WIN32_WINNT 0x0501
#include <Windows.h>
#include <WtsApi32.h>

bool StartInteractiveProcess(LPTSTR cmd, LPCTSTR cmdDir) {
    STARTUPINFO si;
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    si.lpDesktop = TEXT("winsta0\\default");  // Use the default desktop for GUIs
    PROCESS_INFORMATION pi;
    ZeroMemory(&pi, sizeof(pi));
    HANDLE token;
    DWORD sessionId = ::WTSGetActiveConsoleSessionId();
    if (sessionId==0xffffffff)  // Noone is logged-in
        return false;
    // This only works if the current user is the system-account (we are probably a Windows-Service)
    HANDLE dummy;
    if (::WTSQueryUserToken(sessionId, &dummy)) {
        if (!::DuplicateTokenEx(dummy, TOKEN_ALL_ACCESS, NULL, SecurityDelegation, TokenPrimary, &token)) {
            ::CloseHandle(dummy);
            return false;
        }
        ::CloseHandle(dummy);
        // Create process for user with desktop
        if (!::CreateProcessAsUser(token, NULL, cmd, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, cmdDir, &si, &pi)) {  // The "new console" is necessary. Otherwise the process can hang our main process
            ::CloseHandle(token);
            return false;
        }
        ::CloseHandle(token);
    }
    // Create process for current user
    else if (!::CreateProcess(NULL, cmd, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, cmdDir, &si, &pi))  // The "new console" is necessary. Otherwise the process can hang our main process
        return false;
    // The following commented lines can be used to wait for the process to exit and terminate it
    //::WaitForSingleObject(pi.hProcess, INFINITE);
    //::TerminateProcess(pi.hProcess, 0);
    ::CloseHandle(pi.hProcess);
    ::CloseHandle(pi.hThread);
    return true;
}
#定义WINVER 0x0501
#定义_WIN32_WINNT 0x0501
#包括
#包括
bool StartInteractiveProcess(LPTSTR cmd、LPCTSTR cmdDir){
STARTUPINFO si;
零内存(&si,sizeof(si));
si.cb=sizeof(si);
si.lpDesktop=TEXT(“winsta0\\default”);//使用GUI的默认桌面
处理信息;
零内存(&pi,sizeof(pi));
处理令牌;
DWORD sessionId=::WTSGetActiveConsoleSessionId();
if(sessionId==0xffffffff)//没有人登录
返回false;
//这仅在当前用户是系统帐户时有效(我们可能是Windows服务)
手柄假人;
if(::WTSQueryUserToken)(
#define WINVER 0x0501
#define _WIN32_WINNT 0x0501
#include <Windows.h>
#include <WtsApi32.h>

bool StartInteractiveProcess(LPTSTR cmd, LPCTSTR cmdDir) {
    STARTUPINFO si;
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    si.lpDesktop = TEXT("winsta0\\default");  // Use the default desktop for GUIs
    PROCESS_INFORMATION pi;
    ZeroMemory(&pi, sizeof(pi));
    HANDLE token;
    DWORD sessionId = ::WTSGetActiveConsoleSessionId();
    if (sessionId==0xffffffff)  // Noone is logged-in
        return false;
    // This only works if the current user is the system-account (we are probably a Windows-Service)
    HANDLE dummy;
    if (::WTSQueryUserToken(sessionId, &dummy)) {
        if (!::DuplicateTokenEx(dummy, TOKEN_ALL_ACCESS, NULL, SecurityDelegation, TokenPrimary, &token)) {
            ::CloseHandle(dummy);
            return false;
        }
        ::CloseHandle(dummy);
        // Create process for user with desktop
        if (!::CreateProcessAsUser(token, NULL, cmd, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, cmdDir, &si, &pi)) {  // The "new console" is necessary. Otherwise the process can hang our main process
            ::CloseHandle(token);
            return false;
        }
        ::CloseHandle(token);
    }
    // Create process for current user
    else if (!::CreateProcess(NULL, cmd, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, cmdDir, &si, &pi))  // The "new console" is necessary. Otherwise the process can hang our main process
        return false;
    // The following commented lines can be used to wait for the process to exit and terminate it
    //::WaitForSingleObject(pi.hProcess, INFINITE);
    //::TerminateProcess(pi.hProcess, 0);
    ::CloseHandle(pi.hProcess);
    ::CloseHandle(pi.hThread);
    return true;
}
if (workDir != null)
   Directory.SetCurrentDirectory(workDir);
C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe  "%~dp0MySoft.exe"