Пару лет назад я решил испытать Remoting в сочетании с winapi и сделать удаленное управление мышью. Решение должно состоять из 2 приложений, взаимодействующих через .NET Remoting. Серверное приложение должно быть в виде службы Windows.
Сервер аналогичен RAdmin серверу, слушает порт и ждёт подключения клиента. Когда клиент подключается, сервер принимает информацию о необходимости изменения положения мыши или нажатии кнопки.
Клиент, в свою очередь, подключается к указанному серверу и, в случае изменения положения мыши в окне клиента, передает информацию на сервер.
На данный момент я не пользуюсь Windows, поэтому восстановлю всё по памяти.
Я использовал Remoting, т.к. на тот момент это была новая интересная технология, позволяющая абстрагироваться от уровня сокетов и общаться клиенту и серверу на уровне объектов/интерфейсов. То есть, вместо того чтобы открывать сокет, обмениваться сообщениями мы просто объявляем объект, который хотим расшарить на сервере и используем его методы на клиенте. Если сравнить сокеты с SQL и непосредственным взаимодействием с БД, то .NET Remoting похож на ORM, красивую и удобную обёртку.
Клиент прост до безобразия. При запуске инициализируем Remoting, при нажатии кнопки подключаемся к серверу или отключаемся от него. Есть окно и форма на нём. Перехватываем движения мыши по ней и нажатие/отпускание клавиши мыши. Клиент знает только интерфейс, с которым он взаимодействует, передает ему в функциях изменения у нашей мышки.
Везде в статье – код на шарпе.
При запуске – загружаем конфигурацию, затем, при клике на кнопку подключаем удаленный объект. При движении и клике вызываем соответствующую функцию этого объекта.
Сервер чуть похитрее. У него есть объект, реализующий выше упомянутый интерфейс, с помощью .NET Remoting он получает информацию об изменениях у мышки и сразу же посылает winapi SendInput для изменения положения мыши и нажатости ее кнопки (для нескольких кнопок мыши, передачи нажатий клавиш или изображения экрана очень просто изменить данный код – в аргументах передаем на сервер, а в возвращаемом значении передаем клиенту).
Сервер оформлен в виде службы Windows (при создании шаблона выбираем service).
В таком случае функция main выглядит так:
Службы это такие же приложения, но они запускаются отдельно, имеют некоторые привилегии и специальные функции для старта/остановки. Такой особый жизненный цикл. Так как эта служба должна запускаться от имени нашего пользователя (чтобы двигать курсор) – меняем пользователя на текущего вручную в панели управления службами.
В нашем случае при запуске службы должна производиться инициализация.
Здесь мы видим функцию запуска службы, в которой инициализируется Remoting. Объяснять особо нечего, загружаем конфиг, регистрируем канал, регистрируем объект. Можно использовать HTTP вместо TCP.
Функции управления курсором я взял готовые. Кажется, здесь.
И завернул их в Communication:
Всё, на сервере больше ничего не нужно, т.к. этот объект будет управляться через .NET Remoting
Есть вариант сделать специальный установщик для этой службы, но на мой взгляд проще создать 2 ярлыка с подобным содержимым:
Для установки:
Для удаления:
Данное приложение было протестировано на компьютерах с Windows XP SP2. На более старших версиях Windows (Vista, 7), по-моему, есть какие-то глюки из-за безопасности, но это решаемо.
Здесь не описан механизм обратной связи сервера с клиентом. Да, можно сделать 2 соединения – прямое и обратное или периодически опрашивать сервер, но это не очень элегантно. Если вам это будет интересно, то элегантное решение я покажу в следующей статье на данную тему. Там будем управлять точкой в трёхмерном пространстве таким образом, чтобы расчеты велись на сервере, а отображение велось на клиенте. А затем клиентскую часть перенесем на Silverlight (уже без Remoting, т.к. ограничения сильвера не дают его использовать) и отобразим в браузере.
Исходники можно скачать здесь.
Сервер аналогичен 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, т.к. ограничения сильвера не дают его использовать) и отобразим в браузере.
Исходники можно скачать здесь.