在不使用 Windows 表单的情况下创建远程桌面客户端应用程序 (C#)
Creating a Remote Desktop Client Application without using Windows Forms (C#)
我需要用 C# 构建一个远程桌面客户端应用程序,它建立到远程 Windows 服务器的连接,然后以编程方式为远程 PC 启动一些服务。
重要的是,当我登录时,服务器端的桌面环境存在,因为我想启动的服务使用它,但在客户端我不想要任何 Windows表单容器,因为我想动态创建这些会话。
为了更好地理解这个问题,假设我想使用控制台应用程序建立远程桌面连接。
重点是,在客户端我不需要任何 GUI,但主机端的服务需要 windows、鼠标、Internet Explorer 等 UI 句柄。
到目前为止,我尝试使用 MSTSClib 创建一个 RdpClient,如 here 所述,但这没有用,因为它使用了 Windows Forms 相关的 AxHost。
关于这是否可能以及我如何实现的任何想法?
更新:
试过这个:
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 论坛:https://social.msdn.microsoft.com/Forums/vstudio/en-US/6c8a2d19-a126-4b4b-aab7-0fa4c22671ed/hosting-remote-desktop-connection-in-wpf-app?forum=wpf
你可以尝试这样的事情(这似乎基于你的研究):
try
{
axMsRdpClient.Server = ServerName;
axMsRdpClient.DesktopHeight = 768;
axMsRdpClient.DesktopWidth = 1024;
axMsRdpClient.Connect();
}
catch (Exception Ex)
{
MessageBox.Show(Ex.Message);
}
您尝试解决的问题听起来像是网络服务解决方案的教科书案例。
您应该在服务器上有一个应用程序 运行,它是一个 Web 服务,正在等待请求。
您的客户端应用程序(控制台应用程序等)向 Web 服务发送调用以请求服务器采取某些操作。
服务器上的应用程序接收请求并执行所需的任务。
您是否出于某些特定原因希望能够从客户端访问服务器上的鼠标等?
最后,我发布了这个问题的答案。
这是远程控制库的包装器,以及 WinForms-like 消息循环。您仍然需要引用 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
}
这就是我调用 Connect 方法的方式
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);
}
这对于其他类型的 ActiveX 控件也可能有用。
我需要用 C# 构建一个远程桌面客户端应用程序,它建立到远程 Windows 服务器的连接,然后以编程方式为远程 PC 启动一些服务。
重要的是,当我登录时,服务器端的桌面环境存在,因为我想启动的服务使用它,但在客户端我不想要任何 Windows表单容器,因为我想动态创建这些会话。
为了更好地理解这个问题,假设我想使用控制台应用程序建立远程桌面连接。 重点是,在客户端我不需要任何 GUI,但主机端的服务需要 windows、鼠标、Internet Explorer 等 UI 句柄。
到目前为止,我尝试使用 MSTSClib 创建一个 RdpClient,如 here 所述,但这没有用,因为它使用了 Windows Forms 相关的 AxHost。
关于这是否可能以及我如何实现的任何想法?
更新:
试过这个:
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 论坛:https://social.msdn.microsoft.com/Forums/vstudio/en-US/6c8a2d19-a126-4b4b-aab7-0fa4c22671ed/hosting-remote-desktop-connection-in-wpf-app?forum=wpf
你可以尝试这样的事情(这似乎基于你的研究):
try
{
axMsRdpClient.Server = ServerName;
axMsRdpClient.DesktopHeight = 768;
axMsRdpClient.DesktopWidth = 1024;
axMsRdpClient.Connect();
}
catch (Exception Ex)
{
MessageBox.Show(Ex.Message);
}
您尝试解决的问题听起来像是网络服务解决方案的教科书案例。
您应该在服务器上有一个应用程序 运行,它是一个 Web 服务,正在等待请求。
您的客户端应用程序(控制台应用程序等)向 Web 服务发送调用以请求服务器采取某些操作。
服务器上的应用程序接收请求并执行所需的任务。
您是否出于某些特定原因希望能够从客户端访问服务器上的鼠标等?
最后,我发布了这个问题的答案。 这是远程控制库的包装器,以及 WinForms-like 消息循环。您仍然需要引用 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
}
这就是我调用 Connect 方法的方式
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);
}
这对于其他类型的 ActiveX 控件也可能有用。