EnglishRussian

Слушаю подкаст Радио-Т в прямом эфире, и пишу о кастомной сериализации объектов, в приложениях.

Как я делал давно-давно? Делал я просто, сохраняя текстовый файлик в виде ключ=значение по одному в строку. Работает это хорошо, но саппортить тяжело, легко ошибиться :(

Второй вариант, который я недавно использовал – сохранение ini файла через WinApi, который работает довольно быстро, делает правильные файлики, но все-же ошибиться и там можно :( Код класса:

public class IniWorker
{
[DllImport("kernel32")]
private static extern long WritePrivateProfileString(string section, string key, string val, string filePath);

[DllImport("kernel32")]
private static extern int GetPrivateProfileString(string section,
string key,string def, StringBuilder retVal, int size, string filePath);

public string FilePath
{
get;
protected set;
}

public IniWorker(string Path)
{
this.FilePath = Path;
}

public void IniWriteValue(string Section, string Key, string Value)
{
WritePrivateProfileString(Section, Key, Value, this.FilePath);
}

public string IniReadValue(string Section, string Key)
{
StringBuilder temp = new StringBuilder(255);
GetPrivateProfileString(Section, Key, string.Empty, temp, 255, this.FilePath);
return temp.ToString();
}

public string IniReadValue(string Section, string Key, string DefaultValue)
{
StringBuilder temp = new StringBuilder(255);
GetPrivateProfileString(Section, Key, string.Empty, temp, 255, this.FilePath);
return string.IsNullOrEmpty(temp.ToString()) ? DefaultValue : temp.ToString();
}

public static void IniWriteValue(string Section, string Key, string Value, string FilePath)
{
WritePrivateProfileString(Section, Key, Value, FilePath);
}

public static string IniReadValue(string Section, string Key, string FilePath, string DefaultValue)
{
StringBuilder temp = new StringBuilder(255);
GetPrivateProfileString(Section, Key, string.Empty, temp, 255, FilePath);
return string.IsNullOrEmpty(temp.ToString()) ? DefaultValue : temp.ToString();
}

}

Примеры вызовов методов я приводить не буду – думаю и так понятно.

Третий вариант, который я применял на днях, отличается своей безопасностью в смысле того, что ошибиться довольно тяжело, однако он не слишком прост, хотя в моей случае он подходил хорошо: вариант этот использует рефлексию. Этот вариант я использовал даже с помощью атрибутов, чтобы задавать имена свойств. Пример кода, атрибут:

class OptionsAttribute: Attribute
{
public OptionsAttribute(string Name)
{
this.Name = Name;
}

public string Name { get; protected set; }
}

Сам класс использования сериализации, правда я делал вручную для малого количества свойств с парочкой типов:

class Options
{
public Options()
{
this. PingTimeOut = 256;
this.Load();
}

[OptionsAttribute("PingTimeOut")]
public int PingTimeOut { get; set; }

private void Load()
{
string path = Path.Combine(Application.StartupPath, "options.ini");
if (!File.Exists(path))
{
return;
}

string[] items = File.ReadAllLines(path, Encoding.UTF8);

PropertyInfo[] properties = typeof(Options).GetProperties(BindingFlags.Instance | BindingFlags.Public);

foreach (PropertyInfo property in properties)
{
OptionsAttribute attribute = property.GetCustomAttributes(typeof (OptionsAttribute), false).OfType<OptionsAttribute>().FirstOrDefault();
if (attribute == null)
{
continue;
}

for (int i = 0; i < items.Length; i++)
{
try
{
if (items[i].StartsWith(attribute.Name + "="))
{
string stringValue = items[i].Substring(attribute.Name.Length + 1);
object value = null;

if (property.PropertyType == typeof(int))
{
value = int.Parse(stringValue);
}
else if (property.PropertyType == typeof (bool))
{
value = bool.Parse(stringValue);
}
else
{
value = stringValue;
}

property.SetValue(this, value, null);
}
}
catch (Exception ex)
{
}
}
}

}

public void Save()
{
List<string> configuration = new List<string>();

PropertyInfo[] properties = typeof(Options).GetProperties(BindingFlags.Instance|BindingFlags.Public);

foreach (PropertyInfo property in properties)
{
OptionsAttribute attribute = property.GetCustomAttributes(typeof (OptionsAttribute), false).OfType<OptionsAttribute>().FirstOrDefault();
if (attribute == null)
{
continue;
}

configuration.Add(attribute.Name + "=" + property.GetValue(this, null).ToString());
}

File.WriteAllLines(Path.Combine(Application.StartupPath, "options.ini"), configuration.ToArray(), Encoding.UTF8);
}
}

Вот такие варианты для сериализации в простой текстовый файл. С сериализацией в XML ситуация другая, и она не для этого поста.

На этом все.


Совсем недавно, буквально вчера появилась интересная задача, определения наличия подключения к интернету. Обычно эта проверка выполняется в консоле, через команду ping.

При решении этой задачи, я сначала подумал, пойти прямо, и решить ее в лоб, посредством создания процесса, без окна, перенавравлять вывод в консоль в переменную и парсить результат, но погуглив нашелся замечательный класс Ping в System.Net.NetworkInformation куда обычно мало кто добирается.

Класс этот хороший и простой, но для его использования для решения поставленной задачи лучше написать обертку, поскольку сам класс уж очень простой. Сама обертка:

public class Pinger
{
private Ping ping;
private PingOptions options;

/// <summary>
/// Gets or sets timeout in milliseconds
/// </summary>
public int TimeOut { get; set; }

public Pinger()
: this(new PingOptions(128, true))
{

}

public Pinger(PingOptions Options)
{
this.ping = new Ping();
this.options = Options;

this.TimeOut = 256;
}

public IPStatus Check(string Host)
{
// Create a buffer of 32 bytes of data to be transmitted.
string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
byte[] buffer = Encoding.ASCII.GetBytes(data);

PingReply reply = this.ping.Send(Host, this.TimeOut, buffer, this.options);

return reply != null ? reply.Status : IPStatus.Unknown;
}

public void WaitForConnection(string Host)
{
while (this.Check(Host) != IPStatus.Success)
{
Thread.Sleep(100);
}
}
}

Что умеет приведенный выше класс? Он умеет пингнуть удаленный хост или ip. А также может ожидать появления соединения с хостом. Если вы заметили, то в классе явно указывается таймаут для запроса. Дело в том, что удаленная машина пингуется всего один раз, и если она очень далеко, и таймаут выходит, то на выходе мы получаем статус ошибки по таймауту. Например в документации, в примере, был указан таймаут 120 милисекунд, при котором ya.ru не пинговался по таймауту.

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


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

Если у вас стоит задача реализовать редактор текста на C# с подсветкой текста, то после первых десяти запросов, вы скорее всего найдете исходники готового компонента, или хотя бы куски кода, показывающего нужное направление, а направление это не хитрое.

Для реализации подсветки синтаксиса нужно использовать компонент RichTextBox, или сделать свой, унаследованный от него, что предпочтительнее, поскольку позволяет использовать дополнительные возможности, которые недоступны снаружи. Кроме RichTextBox понадобиться хоть какое-то знание регулярных выражений, кроме случаев, когда все элементы, которые нужно подсвечивать состоят из одного слова. И третьим элементом, завершающим необходимый функционал, который, по правде сказать, не обязательный, если вам наплевать на эстетику происходящего – это, опять же, хоть какое-то знание WinApi, которое поможет вам избавиться от бликов перерисовки самого редактора.

Идея подсветки синтаксиса состоит в следующем: при каждом изменении текста берем весь текст, и сканируем на наличие нужных элементов, их и подсвечиваем, меняем шрифт если нужно, и т.д.. Подсвечивать можно даже встроеными в RichTextBox методами. У него есть пару методов, с помощью которого можно сделать примитивную подсветку, но я ее настоятельно не рекомендую, поскольку она жутко медленная, и без использования WinApi вы получите большое количество морганий редактора, когда он будет перерисовываться. Для обхода этой проблемы нужно делать следующее: Взять весь текст, в виде простого текста, и пересобрать за один подход его в RTF. К счастью формат RTF не сильно сложен, и для реализации подсветки нужно не так уж и много элементов RTF. После чего полученный RTF нужно присвоить соответствующему свойству редактора.

Однако, кудаже без этого волшебного слова, RTF имеет особенности, например, у него есть таблица цветов и шрифтов, где соответственно содержаться списки используемых в документе цветов и шрифтов. Эти цвета и шрифты имеют индексы, начинающиеся с нуля, однако нулевой цвет, он же черный цвет, всегда нулевой, этот цвет всегда подразумевается, и его в таблице нет, да и записать туда черный цвет нельзя, по этому, фактически использование цветов, кроме черного, начинаются с единицы.

Выпадающий список элементов в текстовом редакторе, в котором располагаются какие-либо стандартные элементы текста, для их удобной и быстрой вставки также очень и очень полезны, особенно если этот список поддерживает фильтрацию своего содержимого по мере ввода дополнительных символов. Такой список нужен когда работа идет с текстами специальной конструкции, такой как искусственные языки, например со всем известным HTML. Сделать такой выпадающий список, на первый взгляд, также довольно не сложно, и действительно это не сложно, пока, пока не наступает момент возни с разными нажатиями на клавиши стрелочек, backspace, enter, переключением фокуса между списком и редактором и наоборот. Ох, не легкое это дело, писать интерфейс. Для решения такой задачи нужно на основе редактора текста делать один цельный компонент, который будет содержать внутри себя довольно сложную логику обрабатывания пользовательских действий.

Думаю на этом все, закончу на этом описание прелестей написания интерфейсов, к тому же Word подсказывает, что написано почти 500 слов.

На этом все.


В последнюю неделю-полторы немного занимался созданием всяких-разных интересных штук в плане интерфейсов пользователей. Много из этого времени уделил WPF под .Net 4.

Что я могу сказать про WPF. Штука интересная, позволяет делать много интересных и замечательных штуковин и вещей. Думаю даже использовать WPF для некоторых целей, пока небольших, но важных.

Из особенностей стоит отметить совершенно другой подход, который и похож, и не очевиден, что называется не интуитивно понятен. Единственное что огорчает, так это отсутствие поддержки старых фич, хочу отметить что частичная поддержка есть, но этого маловато. И мне например пришлось писать специальную прокладку для такой штуковины как меню. Скажу что у меня не простое было, а в принципе самодостаточное, не привязанное к окну. Дело в том, что если не делать прокладку, которой, готовой, в интернете не оказалось. Так вот, класс, длиной около 100 строк выполнил мою задачу, преобразуя WinForms меню в WFP меню, с поддержкой картинок, чекбоксов и пробросом событий. Хорошо получилось.

Пришлось однако помучатся с картинками, поскольку они совсем другие.

Вторая часть поста, которую я сейчас пишу, отстоит во времени где-то на неделю. Вот сколько долго лежала первая часть у меня на рабочем столе, и все никак к ней руки не доходили, да и писать нечего было, да вот нашлось.

Занимался построением компонент для WinForms, если быть точнее, то делал на основе RichTextBox редактор текста, с нужной мне подсветкой, выпадающей менюшкой со всякими пред-установленными значениями, и нумерацией строк. Занятие я вам скажу еще то. Приходится копаться в таких дебрях, что аж страшно :)

Подсветку я пока не сделал, потому что другие вещи много занимали времени, а именно, те о которых пойдет речь далее. Нашел в Интернете практически готовый класс, который реализовывал нужный мне редактор текста с нумерацией строк. Нужен он мне был для того, что я просто люблю, когда пронумеровано, так удобнее. Был он в принципе неплохим, но содержал несколько багов, один из которых был для меня критичным. Баг этот заключался в том, что при изменении коэффициента увеличения текста, то есть Zoom, полосочка с номерами строк оставалась такой-же, а текст, то приближался к ней и прятался под ней, то от нее уезжал. Решений этой задачи было два, первый – изменение размеров полоски с номерами строк, и второй – модифицирование отступа текста от полоски так, чтобы этот отступ был всегда одинаков, независимо от коэффициента увеличения текста. Копался я там долго, но результат того стоит, выглядит и работает хорошо. WinApi помогает :)

Создание в редакторе текста менюшки с предложениями использования тех или иных заготовленных шаблонов, если хорошо делать, то тоже довольно сложная штука. Ведь по сути менюшка и сам редактор – это две разные сущности, которых нужно соединить так, чтобы это было гармонично, и функционально. Под этим я понимаю умную фильтрацию, отсутствие непонятных звуков, которые да, могут быть при неправильном создании этого всего, и т.д. И того, если делать все с нуля, как это делал я, и не имея опыта, то задача выполняется примерно за день.

А все говорят, что интерфейс делать легко – совсем не легко его делать, когда выходишь за плоскость готовых кусочков.

Ну и напоследок чуть-чуть о том, чем я занимался вчера на работе, и почему вчера я был на работе около 12 часов. Есть у нас такой WCF сервер, куда коннектиться клиент, и узнает есть ли апдейт, чтобы его скачать. После недавних модификаций сервера, старые клиенты дружно падали не работая, и не обновляясь с нового сервера. Вот вчера этим и занимался, чтобы обеспечить возможность обновиться старым клиентам. После изучения дела, оказалось, что по умолчанию WCF сортирует данные, которые через него передаются, по алфавиту, и после добавления новых полей порядок старых изменился, и все поплохело от этого. Пришлось вручную выставлять порядок и долго-долго все это дело тестировать, но все закончилось хорошо, и этот пост на этой замечательной ноте тоже закончится, сейчас.


В C# есть некоторые особенности в работе с NotifyIcon. Это особенности в большинстве случаев незаметны, однако если необходимо наделить эту иконку каким-то более-менее сложным поведением, и при этом чтобы поддержка этого всего не превращалась в жутко неприятное занятие, то тут эти особенности и выходят на поверхность.

У меня была следующая задача – у приложения есть два основных окна, предоставляющих примерно равную функциональности, но различающихся по дизайну. Эти оба окна должны иметь иконку в области уведомлений, и эта иконка должна иметь довольно большое контекстное меню, которое абсолютно одинаковое.

Вариант использования на каждой форме иконку и контекстное меню для нее не подходит по нескольким причинам, таким как: это излишек кода, фактически копированный. Так как код дублированный, то все изменения нужно дублировать, а это конечно, не в два раза больше работы, но больше.

На ум приходит желание взять сделать наследованный класс от NotiyIcon, и тут нас ожидает первый подводный камешек – наследоваться от этой иконки нельзя. Поискавши в закоулках интернета я решил не заморачиваться с WinApi по поводу иконки, а сделать компонент для WinForm, в него закинуть иконку, и в него закинуть контекстное меню для нее. Тут следует отметить, что нужно делать компонент, а не пользовательский элемент интерфейса. Такой компонент работает замечательно, и является одним целостным куском. Такой подход решил задачу.

Еще один подводный камень лежит в месте, на которое вы попадаете, когда хотите, чтобы текст, который появляется при подведении курсора к иконке должен отображать какую-либо динамическую информацию. У меня в 64-битной Windows 7 получилось писать туда не более 64 символов. Больше записать туда не дает исключение. Чтобы решить эту задачу, не открою никакого секрета, если скажу, что нужно использовать специально оформленное окошко, где и будет выводится нужная информация.

Вот такая вот иконка, неподатливая :)


Выдалось 15 свободных минут, думаю успею написать этот пост за 1 раз.

Решил недавно проверить как работает мой софт под Mono в Linux. Скачал чистый дистрибутив Ubuntu последней, установил в Virtual PC, вроде работает все, симпатично, но пользоваться нельзя. Проблемы начались с того, что непонятно куда вводить адрес основной машины, чтобы по сети скачать пару файликов для теста. Через какое-то время зашел на основную машину, клацаньем иконок в окне просмотра сети, скачал файлики, и пробую.

Пробовал я Umax Doorway Studio, не заработало совсем, не нашло библиотек самого фреймворка. Тогда мне посоветовали скать уже готовую виртуальную машину с сайта Mono. Скачал, распаковал, оказалось OpenSuse, пробую, дорген повалился на вызовах WinApi, которые любят контролы табличек.

Потом попробовал Umax Doorway Generator 2, с ним оказалось получше. Он запустился, и показалось мне главное окошко, даже простые операции типа выбора файликов работают. Но на чем-то более-менее серьёзном и он упал, при попытке закрытия приложения посыпалась огромная куча исключений, и так ничего не закрылось.

Подводя итог, скажу, что мой опыт работы с Linux, конечно просто мизерный, но как обычный домохозяин, могу сказать, что работало все стабильно, это касательно самой системы. Касательно Mono – реализации .Net под Linux. Эта штука либо очень сырая, либо надо писать ориентируясь на нее. И того, опытным путем доказано, что софт не работает под Linux.

На этом все.


Не так давно на работе выдали задание, добавить возможность программе получать изображение со сканера. До этого я таким не занимался, у меня и сканера то никогда небыло.

Но раз задание есть – нужно делать, и Google помог. Привел он меня к такой штуковине, именуемой Windows Image Acquisition (далее WIA). WIA представляет собой технологию, позволяющую принимать изображение и даже видео со сканеров, веб-камер и, по идее, других устройств, которые поддерживают данный интерфейс.

Подключить к проекту WIA достаточно легко, нужно просто подключить библиотеку wiaaut.dll в Windows 7, про Windows XP я расскажу ниже. После подключения библиотеки в нужное место нужно поставить директиву:

using WIA;

И использовать код, представленый ниже:

const string wiaFormatJPEG = "{B96B3CAE-0728-11D3-9D7B-0000F81EF32E}";CommonDialogClass wiaDiag = new CommonDialogClass();WIA.ImageFile wiaImage = null;wiaImage = wiaDiag.ShowAcquireImage(WiaDeviceType.UnspecifiedDeviceType, WiaImageIntent.GrayscaleIntent, WiaImageBias.MaximizeQuality, wiaFormatJPEG, true, true, false);WIA.Vector vector = wiaImage.FileData;Image i = Image.FromStream(new MemoryStream((byte[])vector.get_BinaryData()));

Вот и все, картинка у нас. Интересные особенности заключаются при попытке заставить это компилироваться в Windows XP, и мы сталкиваемся с несколькими проблемами.

Первая из них в том, что в XP нет этой библиотеки.

Вторая, в том, что расположение скачаной с сайта Microsoft библиотеки в нужном нам месте, или в системной директории не помогают, поскольку wiaaut.dll есть COM библиотека, и ее нужно зарегистрировать командой REGSVR32 wiaaut.dll.

Третья проблема заключается в том, что не заставиш всех это делать! И тогда нужно сделать вот что. Нужно подключить wiaaut.dll у себя в проекте. Visual Studio создаст Interop прослойку. Эту прослойку (Dll библиотеку) скопировать, и далее в проекте ссылаться не на оригинальную библиотеку, а на эту прослойку.

Вот мы и решили задачу :)


Этот пост будет о том, почему С# такой классный... Хотя, на самом деле, этот пост будет о Extension методах, они же расширяющие методы.

Extension методы это методы, которые призваны расширять функциаональность типов, без необходимости создавать унаследованый тип, и вызывать их над экземпляром типа как буд-то этот метод является частью типа. Например такая замечательная вещь как LINQ построен именно на расширяющих методах.

Основное применение этих методов заключается в том, чтобы расширить функционал наиболее часто используемых в проекте сущностей в двух случаях: Когда наследовать сущность невозможно или очень не желательно. Например дабавить возможность выборки к типу string наследдованием невозможно сделав унаследованый класс.

Когда я еще незнал о существовании этих методов, это было больше года назад. Один человек, который писал на Objective-C для iPhone, показывал мне разные фишки Objective-C, и там была такая возможность, это мне показалось очень крутым, и оно действительно позволяет вынести рутинные действия туда, подальше, и забыть про них :)

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

namespace Extensions{ public static class Extensions { public static int WordCount(this string Input) { return Input.Split(new string[] { " ", ".", "?" }, StringSplitOptions.RemoveEmptyEntries).Length; } } }

Следующий пост будет о том, как научить приложение получать картинку со сканера.


Давно не писал в этот раздел, но вот накопились темы, и то сильно расписывать их не буду :)

В работе с WinForms есть такая интересная особенность. Если форма невидима (Visible = false), то при ее закрытия не произойдет событий FormClosing и FormClosed. Можете на это даже не расчитывать. А еще интереснее то, что если вы были подписаны на события, и они это окно должны обновлять, то при интенсивной фоновой работе, которая вызывает эти собития, у вас будут вылетать исключения, говорящие о том, что окно и контролы уже Disposed. В общем красота :) Так что с этим поосторожней.

Второй минитемой этого поста является XML сериализация, которая не умеет работать со сложными объектами, такими как коллекция, или дерево какое-нибудь. Я нашел набор классов для сериализации и десириализации на CodeProject, которые умеют делать такую штуку, но там XML довольно необычный получается. Вот использую его, после допиливания годится к использованию.

И последней темой этого поста будет интересная особенность C#, и насколько я понял во время гугления Java, которые не позволяют конвертировать класс предок к классу наследнику, что с моей точки зрения странно. К тому же в C# невозможно создать оператор конвертирования из предка в наследника – ошибка компиляции. Вот так вот.

И на последок история о нашей доблестной почте. К счастью моя почтовая активность не приводила ни к каким эксцессам, пока что, но вот у друга случилось. Он покупал iPod на eBay, первый пришел успешно, а второй, который был отправлен USPS Priority, потерялся после того, как покинул США. Тут надо понимать, что трекинг USPS Priority не имеет гарантированного трекинга за пределами штатов, чем и воспользовались на почте, или на таможне. Вот так вот. Shit happens.


По долгу службы пришлось поработать с Asp.Net.

Весьма интересная технология, и сильно непривычная технология. По моему небольшому опыту работы с PHP, эти две технологии сильно отличается, как по идеологии.

С одной стороны Asp.Net сильно напоминает разработку обычного Win-приложения, с другой стороны, если сравнивать все с той-же разработкой Win-приложения, то возможностей сильно меньше. С третьей стороны, все делается сильно легче чем на PHP.

Выводы из поста – выводов нет :)