不使用Windows窗体(C#)创建远程桌面客户端应用程序
我需要用C#构建一个远程桌面客户端应用程序,它建立到远程Windows服务器的连接,然后以编程方式启动到远程PC的一些服务 重要的是,当我登录时,服务器端的桌面环境存在,因为我想启动的服务使用它,但在客户端我不需要任何Windows窗体容器,因为我想动态创建这些会话 为了更好地理解这个问题,假设我想使用控制台应用程序建立远程桌面连接。 关键是,在客户端,我不需要任何GUI,但主机端的服务需要windows、鼠标、internet explorer等UI句柄 到目前为止,我尝试使用MSTSClib创建一个RdpClient,如前所述,但没有帮助,因为它使用了AxHost,AxHost依赖于Windows窗体 如果可能的话,有什么想法吗?我怎样才能做到 更新: 我试过这个:不使用Windows窗体(C#)创建远程桌面客户端应用程序,c#,visual-studio,remote-desktop,terminal-services,C#,Visual Studio,Remote Desktop,Terminal Services,我需要用C#构建一个远程桌面客户端应用程序,它建立到远程Windows服务器的连接,然后以编程方式启动到远程PC的一些服务 重要的是,当我登录时,服务器端的桌面环境存在,因为我想启动的服务使用它,但在客户端我不需要任何Windows窗体容器,因为我想动态创建这些会话 为了更好地理解这个问题,假设我想使用控制台应用程序建立远程桌面连接。 关键是,在客户端,我不需要任何GUI,但主机端的服务需要windows、鼠标、internet explorer等UI句柄 到目前为止,我尝试使用MSTSClib
using System;
using AxMSTSCLib;
using System.Threading;
using System.Windows.Forms;
namespace RDConsole
{
class Program
{
static void Main(string[] args)
{
var thread = new Thread(() =>
{
var rdp = new AxMsRdpClient9NotSafeForScripting();
rdp.CreateControl();
rdp.OnConnecting += (s, e) => { Console.WriteLine("connecting"); };
rdp.Server = "xxx.xxx.xxx.xxx";
rdp.UserName = "Administrator";
rdp.AdvancedSettings9.AuthenticationLevel = 2;
rdp.AdvancedSettings9.ClearTextPassword = "xxxxxxxxxx";
rdp.Connect();
Console.ReadKey();
});
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();
Console.ReadKey();
}
}
}
但我得到一个空引用异常
"Object reference not set to an instance of an object.
请更新与第一条评论相关的问题:) 如果我完全理解你的问题,你可以看看这个MSD论坛: 您可以尝试类似的方法(这似乎是基于您的研究):
您试图解决的问题听起来像是web服务解决方案的教科书案例 您应该在作为web服务的服务器上运行一个应用程序,等待请求 您的客户机应用程序(控制台应用程序,无论什么)向web服务发送调用,请求服务器采取一些操作 服务器上的应用程序接收请求并执行所需的任务
您希望能够从客户端访问服务器上的鼠标等,有什么具体原因吗?最后,我给出了答案。 这是远程控制库的包装,以及类似WinForms的消息循环。您仍然需要引用windows窗体dll并创建一个窗体来承载rdpclient,但现在可以从控制台应用程序、windows服务或其他任何地方运行
using AxMSTSCLib;
public class RemoteDesktopApi
{
#region Methods
public void Connect((string username, string domain, string password, string machineName) credentials)
{
try
{
var form = new Form();
var remoteDesktopClient = new AxMsRdpClient6NotSafeForScripting();
form.Controls.Add(remoteDesktopClient);
form.Show();
remoteDesktopClient.AdvancedSettings7.AuthenticationLevel = 0;
remoteDesktopClient.AdvancedSettings7.EnableCredSspSupport = true;
remoteDesktopClient.Server = credentials.machineName;
remoteDesktopClient.Domain = credentials.domain;
remoteDesktopClient.UserName = credentials.username;
remoteDesktopClient.AdvancedSettings7.ClearTextPassword = credentials.password;
remoteDesktopClient.Connect();
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
#endregion
#region Nested type: MessageLoopApartment
public class MessageLoopApartment : IDisposable
{
#region Fields/Consts
private static readonly Lazy<MessageLoopApartment> Instance = new Lazy<MessageLoopApartment>(() => new MessageLoopApartment());
private TaskScheduler _taskScheduler;
private Thread _thread;
#endregion
#region Properties
public static MessageLoopApartment I => Instance.Value;
#endregion
private MessageLoopApartment()
{
var tcs = new TaskCompletionSource<TaskScheduler>();
_thread = new Thread(startArg =>
{
void IdleHandler(object s, EventArgs e)
{
Application.Idle -= IdleHandler;
tcs.SetResult(TaskScheduler.FromCurrentSynchronizationContext());
}
Application.Idle += IdleHandler;
Application.Run();
});
_thread.SetApartmentState(ApartmentState.STA);
_thread.IsBackground = true;
_thread.Start();
_taskScheduler = tcs.Task.Result;
}
#region IDisposable Implementation
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
#region Methods
public Task Run(Action action, CancellationToken token)
{
return Task.Factory.StartNew(() =>
{
try
{
action();
}
catch (Exception)
{
// ignored
}
}, token, TaskCreationOptions.LongRunning, _taskScheduler);
}
protected virtual void Dispose(bool disposing)
{
if (_taskScheduler == null) return;
var taskScheduler = _taskScheduler;
_taskScheduler = null;
Task.Factory.StartNew(
Application.ExitThread,
CancellationToken.None,
TaskCreationOptions.None,
taskScheduler)
.Wait();
_thread.Join();
_thread = null;
}
#endregion
}
#endregion
}
这对于其他类型的ActiveX控件可能也很有用。到目前为止您尝试了什么?我尝试使用MSTSC.lib创建一个RdpClient,如本文所述,但没有帮助,因为它使用了依赖于Windows窗体的AxHost。请将您尝试的内容添加到初始问题中。你可能还想看看@LorneCash是的,看看这个。当我完成这个项目,我会张贴一个更详细的答案,但现在这将帮助你。请记住,您仍然需要手动引用windows窗体,并创建一个窗体,在该窗体上分配rdpclient。在我的例子中,我开发了一个实现rdp连接的windows服务,因此从未绘制任何表单,并通过WCF与之通信。@LorneCash答案终于发布了,尽管我相信您现在已经找到了解决方法谢谢您的回答,但这没有帮助。为了更好地理解这个问题,假设我想使用控制台应用程序建立远程桌面连接。这样我就不能使用AxMSTSCLib,因为它需要Windows窗体。请您编辑您的问题并澄清一下好吗?您是对的,我需要在远程桌面上这样做的原因是我想让多个用户同时在VM上登录。这是我可以通过RD实现的。我仍然有点困惑。web服务可以同时处理来自多个客户端的请求,Windows远程桌面也可以同时处理多个用户登录。我很难找出您试图解决的问题。是的,问题是我想在需要运行windows UI的windows上执行自动化任务。任务可能包括OCR、点击网页内的Javascript按钮等。这就是重点。目前,我对windows窗体上的一些不可见activeX控件感到满意,但我正在探索更大规模的可能性。感谢您的详细介绍。我仍然建议您考虑使用Web服务来允许自动化任务的“开机”,然后在服务器上本地运行它们。web服务可以在请求传入时对其进行排队。这将使体系结构更安全,更易于维护。
using AxMSTSCLib;
public class RemoteDesktopApi
{
#region Methods
public void Connect((string username, string domain, string password, string machineName) credentials)
{
try
{
var form = new Form();
var remoteDesktopClient = new AxMsRdpClient6NotSafeForScripting();
form.Controls.Add(remoteDesktopClient);
form.Show();
remoteDesktopClient.AdvancedSettings7.AuthenticationLevel = 0;
remoteDesktopClient.AdvancedSettings7.EnableCredSspSupport = true;
remoteDesktopClient.Server = credentials.machineName;
remoteDesktopClient.Domain = credentials.domain;
remoteDesktopClient.UserName = credentials.username;
remoteDesktopClient.AdvancedSettings7.ClearTextPassword = credentials.password;
remoteDesktopClient.Connect();
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
#endregion
#region Nested type: MessageLoopApartment
public class MessageLoopApartment : IDisposable
{
#region Fields/Consts
private static readonly Lazy<MessageLoopApartment> Instance = new Lazy<MessageLoopApartment>(() => new MessageLoopApartment());
private TaskScheduler _taskScheduler;
private Thread _thread;
#endregion
#region Properties
public static MessageLoopApartment I => Instance.Value;
#endregion
private MessageLoopApartment()
{
var tcs = new TaskCompletionSource<TaskScheduler>();
_thread = new Thread(startArg =>
{
void IdleHandler(object s, EventArgs e)
{
Application.Idle -= IdleHandler;
tcs.SetResult(TaskScheduler.FromCurrentSynchronizationContext());
}
Application.Idle += IdleHandler;
Application.Run();
});
_thread.SetApartmentState(ApartmentState.STA);
_thread.IsBackground = true;
_thread.Start();
_taskScheduler = tcs.Task.Result;
}
#region IDisposable Implementation
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
#region Methods
public Task Run(Action action, CancellationToken token)
{
return Task.Factory.StartNew(() =>
{
try
{
action();
}
catch (Exception)
{
// ignored
}
}, token, TaskCreationOptions.LongRunning, _taskScheduler);
}
protected virtual void Dispose(bool disposing)
{
if (_taskScheduler == null) return;
var taskScheduler = _taskScheduler;
_taskScheduler = null;
Task.Factory.StartNew(
Application.ExitThread,
CancellationToken.None,
TaskCreationOptions.None,
taskScheduler)
.Wait();
_thread.Join();
_thread = null;
}
#endregion
}
#endregion
}
public void ConnectToRemoteDesktop((string username, string domain, string password, string machineName) credentials)
{
RemoteDesktopApi.MessageLoopApartment.I.Run(() =>
{
var ca = new RemoteDesktopApi();
ca.Connect(credentials);
}, CancellationToken.None);
}