在不使用 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 控件也可能有用。