Удаленное управление мышью с помощью .NET Remoting

  • Tutorial
Пару лет назад я решил испытать Remoting в сочетании с winapi и сделать удаленное управление мышью. Решение должно состоять из 2 приложений, взаимодействующих через .NET Remoting. Серверное приложение должно быть в виде службы Windows.

Сервер аналогичен RAdmin серверу, слушает порт и ждёт подключения клиента. Когда клиент подключается, сервер принимает информацию о необходимости изменения положения мыши или нажатии кнопки.

Клиент, в свою очередь, подключается к указанному серверу и, в случае изменения положения мыши в окне клиента, передает информацию на сервер.

На данный момент я не пользуюсь Windows, поэтому восстановлю всё по памяти.

Я использовал Remoting, т.к. на тот момент это была новая интересная технология, позволяющая абстрагироваться от уровня сокетов и общаться клиенту и серверу на уровне объектов/интерфейсов. То есть, вместо того чтобы открывать сокет, обмениваться сообщениями мы просто объявляем объект, который хотим расшарить на сервере и используем его методы на клиенте. Если сравнить сокеты с SQL и непосредственным взаимодействием с БД, то .NET Remoting похож на ORM, красивую и удобную обёртку.

Клиент


Клиент прост до безобразия. При запуске инициализируем Remoting, при нажатии кнопки подключаемся к серверу или отключаемся от него. Есть окно и форма на нём. Перехватываем движения мыши по ней и нажатие/отпускание клавиши мыши. Клиент знает только интерфейс, с которым он взаимодействует, передает ему в функциях изменения у нашей мышки.

Конфигурация Remoting для клиента

<configuration>
   <system.runtime.remoting>
      <application>
      <lifetime leaseTime="20D" sponsorshipTimeout="1H" renewOnCallTime="1D" leaseManagerPollTime="1H" />
        <client>
        <wellknown type="Communication.Communication,Communication" url="tcp://127.0.0.1:8000/Communication.rem" />                                        
        </client>
         <channels>
            <channel ref="tcp" port="0" clientConnectionLimit="20" >
           </channel>
         </channels>        
       </application>
   </system.runtime.remoting>
<appSettings>
   <add key="RemotingUrl" value="tcp://127.0.0.1:8000/Communication.rem"></add>
</appSettings>
</configuration>

Код клиента

Везде в статье – код на шарпе.
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            RemotingConfiguration.Configure("Client.config");
        }

        ICommunication iCommunication;
        bool connected;
        private void button1_Click(object sender, EventArgs e)
        {
            if (!connected)
            {
                iCommunication = (ICommunication)Activator.GetObject(typeof(ICommunication), textBox4.Text);
                connected = true;
                iCommunication.Move(100, 100);
                button1.Text = "Отключиться";
            }
            else
            {
                button1.Text = "Подключиться";
                connected = false;
            }
        }

        private void panel1_MouseMove(object sender, MouseEventArgs e)
        {
            if (!connected) return;
            iCommunication.Move(e.X, e.Y);
        }

        private void panel1_MouseDown(object sender, MouseEventArgs e)
        {
            if (!connected) return;
            iCommunication.Down();
        }

        private void panel1_MouseUp(object sender, MouseEventArgs e)
        {
            if (!connected) return;
            iCommunication.Up();
        }
    }

При запуске – загружаем конфигурацию, затем, при клике на кнопку подключаем удаленный объект. При движении и клике вызываем соответствующую функцию этого объекта.

Сервер


Сервер чуть похитрее. У него есть объект, реализующий выше упомянутый интерфейс, с помощью .NET Remoting он получает информацию об изменениях у мышки и сразу же посылает winapi SendInput для изменения положения мыши и нажатости ее кнопки (для нескольких кнопок мыши, передачи нажатий клавиш или изображения экрана очень просто изменить данный код – в аргументах передаем на сервер, а в возвращаемом значении передаем клиенту).

Конфигурация Remoting для сервера

<configuration>
   <system.runtime.remoting>
      <application>
<lifetime leaseTime="20D" sponsorshipTimeout="1H" renewOnCallTime="1D" leaseManagerPollTime="1H" />
         <service>
            <wellknown
               mode="Singleton"
               type="Communication.Communication,Communication"
               objectUri="Communication.rem"/>
                                                </service>
                                </application>
                </system.runtime.remoting>
</configuration>

Сервер оформлен в виде службы Windows (при создании шаблона выбираем service).

В таком случае функция main выглядит так:
static void Main()
{
    ServiceBase[] ServicesToRun;
    ServicesToRun = new ServiceBase[] { new Service1() };
    ServiceBase.Run(ServicesToRun);
}

Службы это такие же приложения, но они запускаются отдельно, имеют некоторые привилегии и специальные функции для старта/остановки. Такой особый жизненный цикл. Так как эта служба должна запускаться от имени нашего пользователя (чтобы двигать курсор) – меняем пользователя на текущего вручную в панели управления службами.

В нашем случае при запуске службы должна производиться инициализация.

Код сервера

    public partial class Service1 : ServiceBase
    {
        public Service1()
        {
            InitializeComponent();
        }
        Communication communication;
        protected override void OnStart(string[] args)
        {
            System.Runtime.Remoting.RemotingConfiguration.Configure(Application.StartupPath+"\\Server.config");
            System.Runtime.Remoting.Channels.ChannelServices.UnregisterChannel(channel);
            BinaryServerFormatterSinkProvider serverProv = new BinaryServerFormatterSinkProvider();
            serverProv.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
            BinaryClientFormatterSinkProvider clientProv = new BinaryClientFormatterSinkProvider();
            System.Collections.IDictionary props =
                new System.Collections.Hashtable();
            props["port"] = 8625;
            channel = new TcpChannel(props, clientProv, serverProv);
            System.Runtime.Remoting.Channels.ChannelServices.RegisterChannel(channel);
            communication = new Communication();
            ObjRef or = RemotingServices.Marshal(communication, "Communication.rem");
            communication.Move(100, 100);
        }
        TcpChannel channel;
        protected override void OnStop()
        {
        }
    }

Здесь мы видим функцию запуска службы, в которой инициализируется Remoting. Объяснять особо нечего, загружаем конфиг, регистрируем канал, регистрируем объект. Можно использовать HTTP вместо TCP.

Функции управления курсором я взял готовые. Кажется, здесь.
И завернул их в Communication:

class Communication : MarshalByRefObject, ICommunication
    {
         //здесь много кода для движения мышки – вызова winapi user32.dll, кому интересно – есть в исходниках и по ссылке выше
    }

Всё, на сервере больше ничего не нужно, т.к. этот объект будет управляться через .NET Remoting

Установка/удаление службы

Есть вариант сделать специальный установщик для этой службы, но на мой взгляд проще создать 2 ярлыка с подобным содержимым:

Для установки:
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\InstallUtil.exe "G:\current\location\our_service.exe"
Для удаления:
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\InstallUtil.exe "G:\current\location\our_service.exe" /u


Данное приложение было протестировано на компьютерах с Windows XP SP2. На более старших версиях Windows (Vista, 7), по-моему, есть какие-то глюки из-за безопасности, но это решаемо.

Здесь не описан механизм обратной связи сервера с клиентом. Да, можно сделать 2 соединения – прямое и обратное или периодически опрашивать сервер, но это не очень элегантно. Если вам это будет интересно, то элегантное решение я покажу в следующей статье на данную тему. Там будем управлять точкой в трёхмерном пространстве таким образом, чтобы расчеты велись на сервере, а отображение велось на клиенте. А затем клиентскую часть перенесем на Silverlight (уже без Remoting, т.к. ограничения сильвера не дают его использовать) и отобразим в браузере.

Исходники можно скачать здесь.
Поделиться публикацией

Похожие публикации

Комментарии 11

    +1
    Спасибо, топик интересный. Насколько я понимаю .Net Remoting — это предок WCF? Просто со вторым я работал, а использование первого не застал.
      +1
      На сколько я помню — .Net Remoting является предшественником и одной из частей WCF.
      0
      Спасибо Александр, это очень достойная статья.
      Только, жаль что вы выбрали именно Remoting. На сколько я знаю, про него уже многие и думать забыли, включая Майкрософт. Если у вас появится желание, то интересно будет увидить Ваше творени на Silverlight, а лучше уже и на HTML5, в свете недавних событий :)
        +1
        Спасибо. Обязательно будут и статьи с новинками, но я решил пока поднять уже готовые старые материалы, разместить самые интересные здесь, чтобы добро не пропадало. Может пригодится кому-то.
        0
        К сожалению, все самое интересное в этом коде — (опущенное) управление курсором. Потому что написание сервисов банально, а ремотинг настолько устарел, что использовать его уже неприлично.
          0
          Кстати, формально windows-сервис по умолчанию не имеет доступа к рабочему столу, и ему нужно включать allow interact with desktop. И даже это — настоятельно не рекомендуется ms (по очевидным причинам).

          support.microsoft.com/kb/327618
            0
            Точно! Так и думал что здесь что-то забыл) Все верно, однако это учебная задача и целью было написать либо сервис либо драйвер. Я выбрал сервис. И не рекомендовал бы данное приложение оформлять в таком виде, конечно.
              0
              … и выбрали неправильно, с чем вас и поздравляю.
            –1
            Remoting не является никоим образом конкурентом или схожей технологией с Silverlight, HTML5.
            Remoting предшественник WCF.
            На HTML5 вы подобное не реализируете просто потому что не имеете права управления устройствами ввода, а на Silverlight только в out-of-browser и то только если используете interrop.
            А полезность этой статьи небольшая. HelloWord on Remoting. На том же WCF реализуется тривиально.
              0
              >Remoting не является никоим образом конкурентом или схожей технологией с Silverlight, HTML5.
              Я нигде такой глупости не писал.
              >Remoting предшественник WCF.
              Я об этом написал пару комментариев выше.
              >На HTML5 вы подобное не реализируете просто потому что не имеете права управления устройствами ввода, а на Silverlight только в out-of-browser и то только если используете interrop.
              Опять же не знаю, где вы такую глупость прочитали
              >А полезность этой статьи небольшая. HelloWord on Remoting. На том же WCF реализуется тривиально.
              Здесь приведен конкретный пример написания программы удаленного управления рабочим столом с использованием объектной обёртки над сокетами. И это единственная статья на хабре в данном ключе. Надеюсь кому-нибудь да пригодится.
                0
                > Я нигде такой глупости не писал.
                Извините это был ответ на комментарий:
                habrahabr.ru/blogs/net/124491/?reply_to=4132781#comment_4117524
                Но из-за сброса логина запостилось в корень.

                > И это единственная статья на хабре в данном ключе.
                А нет потому что не нужно даром. Remouting динозавр с своим набором особенностей на которые просто нет нужды тратить время. Hello Word одинаково просто получаются и на WCF и на Remouting, но когда вам придётся налаживать двустороннюю связь ещё и с обработкой обрывов, то начнутся пляски, придётся писать свою реализацию канала и лезть на сетевой уровень. Зачем тратить время на устаревшую и больше не развивающуюся платформу. Я достаточно написал на этой платформе и прошелся по всем её граблям, но её время очень давно ушло. Ещё бы WCF Web API зарелизили и можна было б понемного избавляться от SOAP.
                На хабре нет статей также о написании приложений на WinAPI для Windows 3.1, может напишете.

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое