Wednesday, April 16, 2008

Отладка Windows Service в Visual Studio.

Многие проекты в качестве host-среды используют стандартные службы Windows. Но к сожлению, не все разработчики знакомы с эффективными методами отладки служб, поэтому вновь и вновь, открывая решения своих коллег, я вижу там или дублирующее консольное-host приложение или "голый" сервис, который нужно сначала инсталлировать, запустить, а потом присоединяться отладчиком к его процессу. Существует же несколько способов отладки служб и достачное количество информации в сети, описывающей эти способы. Я хочу выделить, как мне кажется, самый краткий и эффективный способ, с которым, можно нажать F5 и спокойно ждать, когда произойдет попадание в нужную точку останова(если конечно в текущей конфигурации сборки определена константа DEBUG).

using System;
using System.ServiceProcess;
using System.Threading;

namespace DebugWindowsService
{
partial class TestService : ServiceBase
{
public TestService()
{
InitializeComponent();
}

protected override void OnStart(string[] args)
{
// TODO: Add code here to start your service.
}

protected override void OnStop()
{
// TODO: Add code here to perform any tear-down necessary to stop your service.
}

static void Main(string[] args)
{
TestService service = new TestService();
#if (!DEBUG)
ServiceBase.Run(service);
#else
service.OnStart(args);
Console.WriteLine("Debugging session was started. OnStop and other windows service specific methods will not be called.");
Thread.Sleep(Timeout.Infinite);
#endif
}
}
}

Как java и .net пишут числа в поток.

Учавствуя в разработке клиентского приложения на Java и серверного на .Net, встретил интересную несовместимость платформ в записи/чтения чисел в/из бинарный поток . Все началось с того, что в первых четырех байтах(int) каждой порции данных посылаемых из приложения, работающего на устройствах типа CLDC, передавался размер этой порции. На серверной же стороне этот размер не рассчитывался правильным образом. Оказалось, что Java записывает(читает) в формате big-endian, а .Net в little-endian.

Для прояснения картины приведу следующие два примера:

1.C#

            MemoryStream stream= new MemoryStream(new byte[4]);
BinaryWriter writer = new BinaryWriter(stream);
int num = 218893066;
Console.WriteLine("Двоич. форма: " + Convert.ToString(num, 2));
Console.WriteLine("Шестн. форма: " + num.ToString("X"));
writer.Write(num);
byte[] arr=stream.ToArray();
Console.WriteLine("Первый байт: "+arr[0]);
Console.WriteLine("Второй байт: " + arr[1]);
Console.WriteLine("Третий байт: " + arr[2]);
Console.WriteLine("Четвертый байт: " + arr[3]);
Console.ReadLine();
2.Java
ByteArrayOutputStream baos = new ByteArrayOutputStream(4);
DataOutputStream dos = new DataOutputStream(baos);
int num= 218893066;
System.out.println("Двоич. форма: "+Integer.toBinaryString(num));
System.out.println("Шестн. форма: "+Integer.toHexString(num));
dos.writeInt(num);
byte[] arr=baos.toByteArray();
System.out.println(""Первый байт: "+arr[0]);
System.out.println("Второй байт: " + arr[1]);
System.out.println("Третий байт: " + arr[2]);
System.out.println("Четвертый байт: " + (arr[3]));
System.in.read();

Результат 1:


Двоич. форма:1101000011000000101100001010
Шестн. форма: D0C0B0A
Первый байт: 10
Второй байт: 11
Третий байт: 12
Четвертый байт: 13


Результат 2:

Двоич. форма:1101000011000000101100001010
Шестн. форма: d0c0b0a
Первый байт: 13
Второй байт: 12
Третий байт: 11
Четвертый байт: 10


Число 2188930066 специально выбрано для примера, что бы не осложнять картину тем, что в Java byte - знаковый байт(signed byte), а в C# byte - безнаковый байт(unsigned byte). В C# есть знаковый байт(sbyte), а вот в Java такого(таких) типа нет(но разговор сейчас не об этом).

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

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using SystemBinaryReader = System.IO.BinaryReader;

namespace Mmg.Ssp.MobileGateway.Entropy.Protocol.Hybrid
{
class BinaryReader : SystemBinaryReader
{
public BinaryReader(Stream input, Encoding encoding) : base(input, encoding)
{
}

public BinaryReader(Stream input) : base(input)
{
}

public override int ReadInt32()
{
byte b1 = ReadByte();
byte b2 = ReadByte();
byte b3 = ReadByte();
byte b4 = ReadByte();
return (b1 << 24) + (b2 << 16) + (b3 << 8) + (b4 << 0);
}
}
}
using System.IO;
using System.Text;
using SystemBinaryWriter = System.IO.BinaryWriter;

namespace Mmg.Ssp.MobileGateway.Entropy.Protocol.Hybrid
{
public class BinaryWriter : SystemBinaryWriter
{
public BinaryWriter(Stream output, Encoding encoding) : base(output, encoding)
{
}

public BinaryWriter(Stream output) : base(output)
{
}

public BinaryWriter()
{
}

public override void Write(int value)
{
OutStream.WriteByte((byte)(value >> 24 & 0xff));
OutStream.WriteByte((byte)(value >> 16 & 0xff));
OutStream.WriteByte((byte)(value >> 8 & 0xff));
OutStream.WriteByte((byte)(value & 0xff));
}
}
}

Или воспользоваться готовыми решениями. Так же можно воспользоваться классами из J#(vjslib.dll) , которые используют формат big-endian (как и java), но здесь возникает проблема в том, что J# является "копией" java и использует знаковые байты, в отличие от C#(да и вообще, по моим ощущениям, Microsoft откажется от поддержки J#, так например, среды для него уже нет в Microsoft Visual Studio 2008), поэтому это наверное не лучшее решение.