通过Win32 API从服务创建Windows会话
我有一个windows服务,可以通过调用“CreateProcessAsUser”函数在用户windows会话中创建可执行文件。只要windows会话已经存在,这就可以正常工作。如果还没有,我希望能够以编程方式创建一个。这可能吗?似乎找不到执行此操作的函数。LogonUser函数如何通过Win32 API从服务创建Windows会话,windows,winapi,Windows,Winapi,我有一个windows服务,可以通过调用“CreateProcessAsUser”函数在用户windows会话中创建可执行文件。只要windows会话已经存在,这就可以正常工作。如果还没有,我希望能够以编程方式创建一个。这可能吗?似乎找不到执行此操作的函数。LogonUser函数如何 您无法从服务创建新会话。会话由操作系统管理。当用户以交互方式登录时,会创建新的解决方案。这并不是我所问问题的解决方案,但如果你明白我的意思的话,正是这个解决方案帮助我通过问这个问题实现了我想要实现的目标 您可以将w
您无法从服务创建新会话。会话由操作系统管理。当用户以交互方式登录时,会创建新的解决方案。这并不是我所问问题的解决方案,但如果你明白我的意思的话,正是这个解决方案帮助我通过问这个问题实现了我想要实现的目标 您可以将windows配置为在启动时自动登录,而不是使用windows服务创建服务器会话。这仍然意味着有人可能会意外注销,但解决了会话消失的主要原因:服务器重新启动。使用以下步骤激活自动登录:
@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不会解决您的问题。