cra*_*lic 2 c# remote-desktop terminal-services visual-studio
我需要用 C# 构建一个远程桌面客户端应用程序,它建立到远程 Windows Server 的连接,然后以编程方式启动远程 PC 的一些服务。
重要的是,当我登录时,服务器端的桌面环境存在,因为我想启动的服务使用它,但在客户端我不想要任何 Windows 窗体容器,因为我想创建这些动态会话。
为了更好地理解这个问题,假设我想使用控制台应用程序建立一个远程桌面连接。关键是,在客户端我不需要任何 GUI,但主机端的服务需要 Windows、鼠标、Internet Explorer 等 UI 句柄。
到目前为止,我尝试使用 MSTSClib 创建一个 RdpClient,如此处所述,但这没有帮助,因为它使用了 AxHost,它依赖于 Windows 窗体。
关于这是否可能的任何想法,我该如何实现?
更新:
试过这个:
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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
但我得到一个空引用异常
"Object reference not set to an instance of an object.
Run Code Online (Sandbox Code Playgroud)
最后,我发布了这个问题的答案。这是远程控制库的包装器,以及类似 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
}
Run Code Online (Sandbox Code Playgroud)
这就是我调用 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);
}
Run Code Online (Sandbox Code Playgroud)
这对于其他类型的 ActiveX 控件也很有用。
| 归档时间: |
|
| 查看次数: |
8613 次 |
| 最近记录: |