通过Win32 API从服务创建Windows会话

通过Win32 API从服务创建Windows会话,windows,winapi,Windows,Winapi,我有一个windows服务,可以通过调用“CreateProcessAsUser”函数在用户windows会话中创建可执行文件。只要windows会话已经存在,这就可以正常工作。如果还没有,我希望能够以编程方式创建一个。这可能吗?似乎找不到执行此操作的函数。LogonUser函数如何 您无法从服务创建新会话。会话由操作系统管理。当用户以交互方式登录时,会创建新的解决方案。这并不是我所问问题的解决方案,但如果你明白我的意思的话,正是这个解决方案帮助我通过问这个问题实现了我想要实现的目标 您可以将w

我有一个windows服务,可以通过调用“CreateProcessAsUser”函数在用户windows会话中创建可执行文件。只要windows会话已经存在,这就可以正常工作。如果还没有,我希望能够以编程方式创建一个。这可能吗?似乎找不到执行此操作的函数。

LogonUser函数如何


您无法从服务创建新会话。会话由操作系统管理。当用户以交互方式登录时,会创建新的解决方案。

这并不是我所问问题的解决方案,但如果你明白我的意思的话,正是这个解决方案帮助我通过问这个问题实现了我想要实现的目标

您可以将windows配置为在启动时自动登录,而不是使用windows服务创建服务器会话。这仍然意味着有人可能会意外注销,但解决了会话消失的主要原因:服务器重新启动。使用以下步骤激活自动登录:

  • 按键盘上的Windows键+R以启动“运行”对话框
  • 键入regedit并按enter键打开注册表编辑器
  • 然后浏览到HKEY\U LOCAL\U MACHINE\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\Winlogon\
  • 设置AutoAdminLogon=1(如果不存在字符串变量,则创建它)
  • 设置DefaultUserName=您的用户名(如果不存在字符串变量,则创建该用户名)
  • Set-DefaultPassword=您的密码(如果不存在字符串变量,则创建密码)
  • 说明摘自本帖:

    @Robert,我知道这是一个老问题,你已经找到了适合你的东西,但就我而言,我一直在寻找类似于你最初问题的东西,我最终找到了答案,所以我想与大家分享。我的解决方案只使用.NET和COM引用,而不是标题中提到的Win32 API,但我猜这并不是您真正需要的

    我已经编写了一个使用远程桌面ActiveX控件(COM参考)的简单实用程序。如果您将此代码粘贴到类库中,那么只需传入服务器、用户名、域和密码即可调用它,一切都已完成,无需进行任何其他交互。方法完成后,您可以调用“CreateProcessAsUser”代码。我编写此实用程序的方式是,您可以每次调用它,但启动RDP会话需要几秒钟,因此出于性能考虑,我建议您编写另一个方法来枚举会话,查看您的用户是否已登录,并且仅在确定您的用户未登录时调用此实用程序(这就是我在实际项目中所做的)。如果你觉得在评论中的那篇文章需要帮助,我会分享我是如何做到的,但这并不是这个问题的一部分,所以现在我不考虑它

    这里有一个指向我的问题的链接,它比这个问题有更多的要求/细节

    这是我的RDP实用程序。将此代码放入类库后,您可以从控制台应用程序、winForms应用程序、运行在同一台计算机上的windows服务或远程计算机调用它

    using System;
    using System.Diagnostics;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using AxMSTSCLib;
    
    namespace Utility.RemoteDesktop
    {
        public class Client
        {
            private int LogonErrorCode { get; set; }
    
            public void CreateRdpConnection(string server, string user, string domain, string password)
            {
                void ProcessTaskThread()
                {
                    var form = new Form();
                    form.Load += (sender, args) =>
                    {
                        var rdpConnection = new AxMSTSCLib.AxMsRdpClient9NotSafeForScripting();
                        form.Controls.Add(rdpConnection);
                        rdpConnection.Server = server;
                        rdpConnection.Domain = domain;
                        rdpConnection.UserName = user;
                        rdpConnection.AdvancedSettings9.ClearTextPassword = password;
                        rdpConnection.AdvancedSettings9.EnableCredSspSupport = true;
                        if (true)
                        {
                            rdpConnection.OnDisconnected += RdpConnectionOnOnDisconnected;
                            rdpConnection.OnLoginComplete += RdpConnectionOnOnLoginComplete;
                            rdpConnection.OnLogonError += RdpConnectionOnOnLogonError;
                        }
                        rdpConnection.Connect();
                        rdpConnection.Enabled = false;
                        rdpConnection.Dock = DockStyle.Fill;
                        Application.Run(form);
                    };
                    form.Show();
                }
    
                var rdpClientThread = new Thread(ProcessTaskThread) { IsBackground = true };
                rdpClientThread.SetApartmentState(ApartmentState.STA);
                rdpClientThread.Start();
                while (rdpClientThread.IsAlive)
                {
                    Task.Delay(500).GetAwaiter().GetResult();
                }
            }
    
            private void RdpConnectionOnOnLogonError(object sender, IMsTscAxEvents_OnLogonErrorEvent e)
            {
                LogonErrorCode = e.lError;
            }
            private void RdpConnectionOnOnLoginComplete(object sender, EventArgs e)
            {
                if (LogonErrorCode == -2)
                {
                    Debug.WriteLine($"    ## New Session Detected ##");
                    Task.Delay(10000).GetAwaiter().GetResult();
                }
                var rdpSession = (AxMsRdpClient9NotSafeForScripting)sender;
                rdpSession.Disconnect();
            }
            private void RdpConnectionOnOnDisconnected(object sender, IMsTscAxEvents_OnDisconnectedEvent e)
            {
                Environment.Exit(0);
            }
        }
    }
    

    在调用
    CreateProcessAsUser()之前,您可以使用WTS API,例如
    WTSGetActiveSessionId()
    WTSEnumerateSessions()
    ,来确定用户会话是否存在
    。请注意:Windows Server 2012支持远程桌面协议提供程序API,您可以使用该API以编程方式创建会话。此外,至少在理论上,您可以编写自己的远程桌面客户端(或修改一个开源客户端)要在任何受支持的Windows版本上创建新会话-当然,前提是启用了远程桌面。@RemyLebeau这是我已经做过的,我正试图避免会话意外关闭时出现的问题。@HarryJohnston谢谢,这听起来像是解决方案,不过我不得不承认我希望有更简单的解决方案。@Robert:你是一个请告诉我两者。如果您确实获得了一个有效的解决方案,并且能够共享它,您可以让我知道吗?(我的个人资料包括我的电子邮件地址。)不幸的是,“会话”在Windows中是一个超载的术语。在这种情况下,我认为OP指的是远程桌面(也称为Windows终端服务)会话而不是登录会话。Robert,你能澄清一下吗?我不确定我是否理解这里的区别,它本身不需要是远程桌面会话,但它确实需要会话ID>0,这样它才能使用gui执行程序。远程桌面服务是Windows的一部分,允许用户切换和会话零隔离,以及实际的远程桌面连接。这里您肯定在谈论远程桌面会话ID,所以LogonUser不会解决您的问题。