【问题标题】:Creating a Remote Desktop Client Application without using Windows Forms (C#)在不使用 Windows 窗体 (C#) 的情况下创建远程桌面客户端应用程序
【发布时间】:2021-09-03 20:22:11
【问题描述】:

我需要使用 C# 构建一个远程桌面客户端应用程序,该应用程序建立与远程 Windows Server 的连接,然后以编程方式启动远程 PC 的一些服务。

重要的是,当我登录时,服务器端的桌面环境存在,因为我要启动的服务会使用它,但在客户端我不想要任何 Windows 窗体容器,因为我想要动态创建这些会话。

为了更好地理解这个问题,假设我想使用控制台应用程序建立远程桌面连接。 关键是,在客户端我不需要任何 GUI,但主机端的服务需要窗口、鼠标、Internet Explorer 等 UI 句柄。

到目前为止,我尝试使用 MSTSClib 创建 RdpClient,如 here 所述,但这没有帮助,因为它使用了依赖于 Windows 窗体的 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.

【问题讨论】:

  • 到目前为止你尝试了什么?
  • 我尝试使用 MSTSC.lib 来创建一个 RdpClient,如此处所述 codeproject.com/Articles/43705/Remote-Desktop-using-C-NET,但这并没有帮助,因为它使用了依赖于 Windows 窗体的 AxHost。跨度>
  • 请将您尝试过的内容添加到您最初的问题中。您可能还想看看this article
  • @LorneCash 是的,看看这个stackoverflow.com/questions/37310418/…。当我完成这个项目时,我会发布一个更详细的答案,但现在这会对你有所帮助。请记住,您仍然需要手动引用 windows 窗体,并创建一个您将在其上分配 rdpclient 的窗体。在我的例子中,我开发了一个实现 rdp 连接的 Windows 服务,因此不会绘制任何窗体,并通过 WCF 与之通信。
  • @LorneCash 答案终于发布了,尽管我相信你现在已经找到了解决方法

标签: c# visual-studio remote-desktop terminal-services


【解决方案1】:

最后,我发布了这个问题的答案。 这是远程控制库的包装,以及类似 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
}

这就是我调用 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 控件也可能很有用。

【讨论】:

    【解决方案2】:

    能否请您更新与第一个 cmets 相关的问题 :)

    如果我完全理解你的问题,你可以看看这个 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);
       }
    

    【讨论】:

    • 感谢您的回答,但这无济于事。为了更好地理解这个问题,假设我想使用控制台应用程序建立远程桌面连接。这样我就不能使用 AxMSTSCLib,因为它需要 Windows 窗体。
    • 您能否编辑您的问题并进行澄清?
    【解决方案3】:

    您尝试解决的问题听起来像是 Web 服务解决方案的教科书案例。

    您应该在服务器上运行一个作为 Web 服务的应用程序,等待请求。

    您的客户端应用程序(控制台应用程序等)向 Web 服务发送调用以请求服务器执行某些操作。

    服务器上的应用程序接收请求并执行所需的任务。

    您是否有某些特定原因希望能够从客户端访问服务器上的鼠标等?

    【讨论】:

    • 你说得对,我需要使用远程桌面执行此操作的原因是我希望多个用户同时登录 VM。这就是我可以通过 RD 完成的事情。
    • 我还是有点困惑。 Web 服务可以同时处理来自多个客户端的请求,Windows 远程桌面也可以处理多个用户同时登录。我无法弄清楚您要解决的问题。
    • 是的,我想在需要运行 Windows UI 的 Windows 上执行自动化任务。可能包括 OCR、点击网页内的 Javascript 按钮等等的任务。这就是重点。目前我对 Windows 窗体上的一些不可见的 activeX 控件感到满意,但我正在探索更大范围内的可能性。
    • 感谢您的详细说明。我仍然建议您考虑使用 Web 服务来允许“启动”自动化任务,然后在服务器上本地运行它们。 Web 服务可以在请求进入时对其进行排队。这将使架构更安全且更易于维护。
    猜你喜欢
    • 1970-01-01
    • 2020-04-24
    • 1970-01-01
    • 1970-01-01
    • 2016-02-16
    • 1970-01-01
    • 1970-01-01
    • 2012-12-21
    • 1970-01-01
    相关资源
    最近更新 更多