Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/284.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
不使用Windows窗体(C#)创建远程桌面客户端应用程序_C#_Visual Studio_Remote Desktop_Terminal Services - Fatal编程技术网

不使用Windows窗体(C#)创建远程桌面客户端应用程序

不使用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

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

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

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

到目前为止,我尝试使用MSTSClib创建一个RdpClient,如前所述,但没有帮助,因为它使用了AxHost,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();
        }


    }
}
但我得到一个空引用异常

"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);
    }