DetecTOR v 0.3.1b

По многочисленным просьбам зрителей обновлена утилита DetecTOR, которая определяет, присутствует ли определенный IP в сети Tor.

— В связи с тем, что база данных Tor’овских IP по адресу http://torstatus.blutmagie.de/query_export.php/Tor_query_EXPORT.csv сдохла, то в качестве источника данных по умолчанию установлена https://check.torproject.org/exit-addresses и формат «Только IP».
В формате «Только IP» утилита пытается вытащить из источника данных все, что похоже на записи адреса IPv4 в полном формате (x.x.x.x, где x — число от 0 до 255).

— Выяснилось, что утилита не работает на Windows XP с IE6. Ошибка для C# программ (и не только их) известная, решение следующее:
Для Windows XP необходимо установить Internet Explorer 8 с обновленными корневыми сертификатами, отдельно или в составе кумулятивного обновления. Обновление можно скачать здесь или здесь

readme.txt

Основная статья о DetecTOR Копия
Скачать (портативная версия)
Исходники

C#, HttpWebRequest: использование самоподписанного (self-signed) сертификата.

Преамбула

Если попытаться соединиться с HTTPS-сервером, имеющим самоподписанный сертификат, то HttpWebRequest сгенерирует исключение WebException Базовое соединение закрыто: Не удалось установить доверительные отношения для защищенного канала SSL/TLS. (The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.). Оно и понятно, HttpWebRequest не смог проверить сертификат узла и закономерно нас послал.

Решение заключается в переопределении глобального обработчика System.Net.ServicePointManager.ServerCertificateValidationCallback.

Внимание! Изменение ServerCertificateValidationCallback повлияет на все соединения в программе, пока обработчик не будет установлен по умолчанию, надо не забывать это, и возвращать обработчик к стандартному значению, после того, как работа с сервером, имеющим самоподписанный сертификат, завершена.

Предположим, есть класс-обертка над HttpWebRequest, надо его дополнить так, чтоб можно было работать с серверами с self-signed сертификатами.

Игнорирование неправильных сертификатов

Самый простой способ, но не самый безопасный. ServerCertificateValidationCallback передается делегат, который всегда возвращает true. Добавляем в класс функцию:

public void EnableIgnoreCertError()
{
    ServicePointManager.ServerCertificateValidationCallback =
        delegate { return true; };
}

И функцию, которая возвращает ServerCertificateValidationCallback к значению по умолчанию:

public void DisableIgnoreCertError()
{
    ServicePointManager.ServerCertificateValidationCallback = 
        null;
}

Начали работать с сервером — вызвали первую, закончили — вызвали вторую.

Самостоятельная проверка сертификата

Немного более сложный, но более безопасный способ. Необходимо заранее либо иметь сам сертификат, либо знать его хэш. В следующем примере будем проверять как раз хэш SHA1 сертификата.
1. Подключим в References’ах System.Security.Cryptography.
2. Подключим необходимые пространства имен:

using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

3. Заведем в классе поле, куда из вызывающей программы будем передавать заранее известный хэш сертификата:

public string CertHashString { get; set; }

4. Заведем две функции, для включения и выключения самостоятельной проверки сертификата:

public void EnableValidateCert()
{
    ServicePointManager.ServerCertificateValidationCallback =
        ValidateCert;
    
}

public void DisableValidateCert()
{
    ServicePointManager.ServerCertificateValidationCallback =
        null;
}

В первой функции вместо делегата, возвращающего true, указываем нашу функцию проверки.

Функция проверки сертификата

Функция проверки сертификата обязательно должна иметь следующий заголовок:
bool FunctionName (object, X509Certificate, X509Chain, SslPolicyErrors), т.е., например:

private bool ValidateCert(object sender, X509Certificate cert,
    X509Chain chain, SslPolicyErrors sslPolicyErrors)

В функции:
1. Проверяем наличие ошибок SSL, если их нет, значит сертификат уже был опознан:

if (sslPolicyErrors == SslPolicyErrors.None)
{
    return true;
}

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

if (string.IsNullOrEmpty(CertHashString))
{
    return false;
}

3. Получаем хэш сертификата сервера в виде строки:

string hashstring = cert.GetCertHashString();

4. Если хэши полученного и известного сертификата совпадают — все ок, возвращаем true, иначе false.

if (hashstring == CertHashString)
{
    return true;
}

return false;

Функция целиком:

private bool ValidateCert(object sender, X509Certificate cert,
    X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
    //все и так хорошо
    if (sslPolicyErrors == SslPolicyErrors.None)
    {
        return true;
    }

    //не передан хэш сертификата
    //значит и проверять нечего
    if (string.IsNullOrEmpty(CertHashString))
    {
        return false;
    }

    //получаем хэш сертификата сервера в виде строки
    string hashstring = cert.GetCertHashString();

    //если хэши полученного и известного 
    //сертификата совпадают - все ок.
    if (hashstring == CertHashString)
    {
        return true;
    }

    return false;
}

Класс целиком на PasteBin
На GitHub

Источник

C#, получение хэша (fingerprint) сертификата X509.

Преамбула

Понадобилось проверить сертификат X509 на целостность и подлинность. Поставщик сертификата отдельно передает его fingerprint и алгоритм хэширования по которому тот вычисляется.

Судя по документации от MS, получить fingerprint, он же хэш, можно функцией GetCertHashString() (или GetCertHash() в виде массива байт) класса X509Certificate2, однако, в .NET 2.0 отсутствует перегрузка функции GetCertHashString(HashAlgorithmName), которая позволяет выбрать алгоритм хеширования (GetCertHashString() возвращает хэш SHA1). Потому сделаем руками, благо ничего сложного в этом нет.

Получение fingerprint

1. Подключаем в References System.Security.Cryptography
2. Подключаем необходимые пространства имен в классе:

using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

3. Загружать сертификат будем из файла, потому проверяем его наличие:

if (!File.Exists(CertFile))
{
    Console.WriteLine("File not found.");
    return null;
}

4. Загружаем сертификат (создаем экземпляр класса X509Certificate2). Если вдруг файл сертификата поврежден, то конструктор X509Certificate2 вызовет Exception Требуемый объект не найден. Поэтому конструктор надо обернуть в try...catch:

try
{
    X509 = new X509Certificate2(CertFile);
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    return null;
}

5. Получаем загруженный сертификат в виде массива байт:

byte[] cert = X509.GetRawCertData();

6. Создаем объект HashAlgorithm, который и займется вычислением хэша/fingerprint’а. Я завел в функции входную переменную AlgName, куда записывается строка с названием алгоритма, что бы иметь возможность выбора алгоритма хэширования если что:

HashAlgorithm alg = null;
switch (AlgName.ToUpperInvariant())
{
    case "MD5": alg = MD5.Create(); break;
    case "SHA1": alg = SHA1.Create(); break;
    case "SHA256": alg = SHA256.Create(); break;
    case "SHA384": alg = SHA384.Create(); break;
    case "SHA512": alg = SHA512.Create(); break;
    default:
        {
            Console.WriteLine("Unknow algorithm.");
            return null;
        }
}

7. Получаем хэш в виде массива байт:

byte[] hash = alg.ComputeHash(cert);

8. Преобразуем массив байт в строку шестнадцатеричных чисел:

string hex = BitConverter.ToString(hash).ToLowerInvariant().
    Replace("-", "");

ToLowerInvariant() преобразует буквы в строке, полученной BitConverter в строчные, а Replace("-", "") удаляет разделители (-) между значениями байтов, которые BitConverter вставляет в строку.

Код функции целиком
Тестовый пример на GitHub

C#, парсинг и прочая работа с JSON. В том числе и в .NET 2.0

Преамбула

Понадобилось достать несколько значений из JSON-файла, искал, чем это сделать и нашел просто офигенную библиотеку Json.NET. Это не просто парсер, это целый набор инструментов: парсер, сериализаторы, десериализаторы, конвертеры. Кроме банального вытаскивания значений, можно формировать JSON, конвертировать его в XML и XML в JSON, обращаться к объектам JSON через запрос SelectToken или LINQ. А можно и просто вытащить нужное значение, обращаясь к полям JSON, используя встроенные функции объектов.

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

Простой парсинг и извлечение значений

С помощью JObject и JToken

1. Подключаем библиотеку для своего Framework’а через References и в исходнике:

using Newtonsoft.Json.Linq;

2. Загружаем JSON в переменную, например из файла.

3. Парсим JSON в JObject:

JObject JSONObj = JObject.Parse(JSONBuf);

JObject.Parse может вызвать exception, если есть синтаксические ошибки в JSON, так что лучше поместить его в try...catch:

public static JObject ParseJSON(string JSONBuf)
{
    JObject JSONObj = null;
    try
    {
        JSONObj = JObject.Parse(JSONBuf);
    }
    catch (Exception ex)
    {
        Console.WriteLine("ERROR: " + ex.Message);
        return null;
    }
    return JSONObj;
}

Пример реакции на ошибку синтаксиса:

Invalid character after parsing property name. Expected ':' but got: {. Path description.languages', line 15, position 9.

4. Далее, можно вытащить значение поля JSON в объект JToken помощью функции JSONObj.GetValue(FieldName);, если поле со значением находится сразу в корне JSON, как, например, api_uri в этом примере, то JToken можно сразу преобразовать в строку с помощью функции .ToString:

JToken tok = JSONObj.GetValue("api_uri");
tok.ToString();

Однако, если поле отсутствует, то это вызовет exception:

В экземпляре объекта не задана ссылка на объект.

Этого можно избежать, отловив исключение в try...catch, либо проверив наличие поля функцией JObject.ContainsKey("имя_поля") перед вызовом GetValue, либо воспользовавшись функцией TryGetValue вместо GetValue

Примеры кода на PasteBin
Тестовый проект

Если же нужное значение находится где-то глубоко в недрах JSON, то необходимо воспользоваться функцией JObject.SelectToken() Пример в официальной документации Копия

Что дальше?

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

Скачать

Json.NET с официального сайта
Копия библиотеки v 120r3

C#, поиск файла по маске.

Или небольшая заметка про странный глюк (на самом деле фичу) MS и кривофикс к ней.

Словил странный баг при поиске файлов по маске, функцией Directory.GetFiles();

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

Т.е. на *.ph оно найдет только файлы с расширением ph, а на *.a — только файлы с расширением a

Этот баг распространяется только на последнее расширение. Если в имени файла есть конструкция типа *.tmp.php, например admin.tmp.php, то при задании маски *.php расширение tmp будет, слава Ктулху, проигнорировано.

Оказалось, что это не баг, а фича, и об этом прямо написано в MSDN:

Если указанное расширение имеет длину ровно три символа, метод возвращает файлы с расширениями, которые начинаются с указанного расширения. Например, «*. xls» возвращает оба значения: «Book.xls» и «Book.xlsx».

Не знаю, как создатели DOS смогли заговнять и испортить самую простую операцию, которая работала у них, как минимум, с 1989 года, но факт. Заговняли.

В общем, я теряюсь в догадках.

Кривофикс

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

1. Создал функцию, которая будет вытаскивать однозначное расширение и из заданной маски, и из поданного на вход файла:

private string GetExtension(string FileName)
{
    FileName = FileName.Replace('*', '_');
    FileName = FileName.Replace('?', '-');
    FileInfo fi = new FileInfo(FileName);
    return fi.Extension;
}

Замены Replace(...) тут для того, чтобы класс FileInfo не выпал в Exeption, если ему подать что-то вида *.html, т.к. FileInfo не принимает имен с недопустимыми символами, к которым относятся и маски подстановки. В определении расширений класс FileInfo такого глюка не имеет, и отличает file.html от file.htm. И тоже не признает двойные расширения, учитывая только последнее.

2. В функции, где будем вызывать поиск файлов, вызываем, собственно, функцию поиска:

string[] files = Directory.GetFiles(Path, Mask, SearchOptions);

3. Получаем расширение маски:

string MaskExt = GetExtension(Mask);

4. Далее обрабатываем выходной массив. Например, тут я добавлял его в List<string> с именем FoundFiles. Мне надо было искать файлы по маскам, и нужно было, чтоб *.htm и, например, *.html различались.

Например, я просто сравнивал расширение от маски файла с расширением от имени файла, и если оно совпадало — добавлял в результирующий список FoundFiles:

string FileExt = GetExtension(filename);
if (FileExt == MaskExt)
{
    FoundFiles.Add(filename);
}

Или относительно полностью:

string MaskExt = GetExtension(Mask);
//[...]
string[] files = Directory.GetFiles(Path, Mask, SearchOptions);
foreach (string filename in files)
{
    string FileExt = GetExtension(filename);
    if (FileExt == MaskExt)
    {
        FoundCtr++;
        FoundFiles.Add(filename);
    }
}

Пакетный конвертер (перекодировщик) текстовых файлов v 0.0.2b

Изначально писалось для товарищей в 2016 г., поскольку почему-то все пакетные конвертеры/перекодировщики текстовых файлов были исключительно платными, во всяком случае на тот момент и под Windows.

Основные возможности

— Пакетная перекодировка текстовых файлов
— Создание полного дерева каталогов для перекодированных файлов
— Доступен расширенный список кодировок (все кодировки, поддерживаемые .NET Framework)

Изменения в версии v 0.0.2b

— Добавлена возможность сохранять изменения в целевой каталог (перезаписывать файлы)
— Программа по умолчанию работает в портативном режиме, настройки хранятся в каталоге с программой.
— Изменен формат файла настроек на XML

Системные требования

Windows XP/7/8/10
.NET Framework 2.0.
256 Мб ОП

Ключи командной строки

/help — эта помощь
/np — отключение портативного режима
(настройки программы хранятся в C:\Users\<пользователь>\AppData\Local\BatchTextConverter\)

Разработчики

D. Larin
Chang Min Ho
PunkArr[]

Скриншоты


Главный экран

Остальные скриншоты

Исходники

Репозиторий на GitHub

Скачать

Портативная версия

C#, передача форме фокуса, если она открыта, и открытие, если ее нет.

Практически идентичное решение уже обсуждалось в заметке «C#, проверить, открыта ли форма» (копия).

Делается все примерно также. Для удобства вынесем проверку, открыта ли форма в отдельную функцию:

private bool IsFormOpen(string FormName)
{
    foreach (Form f in Application.OpenForms)
    {
        if (f.Name == FormName)
        {
            f.Focus();
            return true;
        }
    }
    return false;
}

Если форма открыта, устанавливаем ей фокус (f.Focus()) и возвращаем true. Если формы нет — возвращаем false.

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

private void btnChild1_Click(object sender, EventArgs e)
{
    if (IsFormOpen("frmChild1")) return;
    
    frmChild1 fChild1 = new frmChild1();
    fChild1.Show();
}

private void btnChild2_Click(object sender, EventArgs e)
{
    if (IsFormOpen("frmChild2")) return;

    frmChild2 fChild2 = new frmChild2();
    fChild2.Show();
}

Пример на GitHub

Как вручную распаковать пакет nuget.

И извлечь из него сборки (библиотеки).

Часто бывает, что нужная библиотека лежит на nuget.org. Если у вас современная Visual Studio, то проблем нет — инструментарий nuget доступен, во всяком случае, из командной строки. Можно установить nuget и отдельно, но что делать, если устанавливать ничего не хочется?

На самом деле пакет nuget (файл *.nuget) это обыкновенный ZIP-архив. Поэтому можно скачать пакет, переименовать его в *.zip и открыть любимым архиватором.

Например, нам нужен упомянутый в предыдущем посте HtmlAgilityPack, версии 1.11.15.

1. Переходим по ссылке https://www.nuget.org/packages/HtmlAgilityPack/1.11.15
2. Выбираем справа Download package
3. Сохраняем пакет
4. Меняем ему расширение на zip
5. Открываем любимым архиватором

В архиве в подкаталоге \lib находятся каталоги различных сборок:

Net20
Net35
Net40
Net40-client
Net45
NetCore45
netstandard1.3
netstandard1.6
netstandard2.0
portable-net45+netcore45+wp8+MonoAndroid+MonoTouch
portable-net45+netcore45+wpa81+wp8+MonoAndroid+MonoTouch
uap10.0

C# Парсер HTML (под .NET Framework 2.0).

Понадобился тут для одного проекта все-таки полноценный парсер HTML, да еще желательно чтоб под не очень свежий .NET Framework 2.0. Ну не хочу я ради небольшого проекта жертвовать совместимостью да и переходить на новый .NET вообще, потому что в старом все прекрасно работает.

В общем выбирал-выбирал и остановился на HtmlAgilityPack, который в версии 1.11.15 поддерживает в т.ч. и .NET 2.0. Версия 1.11.16 .NET 2.0 уже не поддерживает, все остальное, начиная с .NET 3.5 есть.

Скачать можно через NuGet. Как быть тем, у кого NuGet нет читать здесь.

Прямая ссылка
Обсуждение на StackOwerflow

C#, проверить, открыта ли форма.

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

Например, такой код при нажатии на кнопку создаст столько форм frmChild, сколько раз кнопка будет нажата:

private void btnOpenForm_Click(object sender, EventArgs e)
{
    frmChild fChild = new frmChild();
    fChild.Show();
}

Конечно, можно сделать форму модальной, с помощью fChild.ShowDialog(), тогда мы не будем иметь доступа к первой форме, пока вторая не закроется. Но если это не нужно, например, мы хотим свободно переключаться между двумя формами, но вторую создать только один раз.

Открытые формы содержатся в Application.OpenForms. И фактически, надо просто перебрать этот массив, чтобы посмотреть, открыта или нет форма, например по ее имени:

foreach (Form f in Application.OpenForms)
    {
        if (f.Name == "frmChild")
        {
            lblFormOpened.Text = "Form #2 already opened!";
            return;
        }
    }

Код обработчика нажатия кнопки целиком:

private void btnOpenForm_Click(object sender, EventArgs e)
{
    lblFormOpened.Text = "";
    
    foreach (Form f in Application.OpenForms)
    {
        if (f.Name == "frmChild")
        {
            lblFormOpened.Text = "Form #2 already opened!";
            return;
        }
    }
    frmChild fChild = new frmChild();
    fChild.Show();
}

Источник
Код примера на GitHub

Аццкий погодный информер.

Не, он и правда настолько аццкий и быдлокод, что я всем его даже не покажу. Редкий случай, когда мне почти стыдно. Там все, и наглое выдирание данных прямо из HTML, и стукнутое преобразование их в таблицу, CSV, а потом и DataSet, для дальнейшего анализа, и прочее, и прочее. Если дизайнер изменит сайт, с которого берется погода, то оно работать не будет, и не факт, что я буду это переделывать.

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

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

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

C# Всплывающая форма над областью уведомлений, делаем сами.

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

На самом деле все оказывается довольно просто, и даже Ктулху вызывать пользоваться WinAPI не надо.

На первом этапе моделируем будущую всплывающую форму. Я, например, создал форму без границ (FormBorderStyle = none), поместил на нее TextBox, который будет отображать сообщение и PictureBox, который будет работать в качестве кнопки закрытия формы.

На форму добавляем необходимые компоненты, которые отвечают за отображение информации и таймер (tmrAni), который будет отвечать за анимацию всплытия формы.

1. Заводим переменные, сохраняющие позиции формы:

private int StartPosX; private int StartPosY;

Ну я еще добавил переменную для сообщения и экспортировал функцию WinAPI, прячущую текстовый курсор:

[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern bool HideCaret(IntPtr hWnd);
public string MessageText = "";

Плюс настроил в инициализации формы некоторые ее параметры:

public frmPopup()
{
    InitializeComponent();
    //Настройка формы
    this.TopMost = false;
    this.ShowInTaskbar = false;
}

TopMost = false нужен, чтоб форма всплывала из-за области уведомлений, а не загораживала ее собой.

2. Переопределяем обработчик события Load и в нем прячем форму за экран:

protected override void OnLoad(EventArgs e)
{
    //Прячем форму за экран
    StartPosX = Screen.PrimaryScreen.WorkingArea.Width - this.Width;
    StartPosY = Screen.PrimaryScreen.WorkingArea.Height;
    SetDesktopLocation(StartPosX, StartPosY);
    base.OnLoad(e);
    //запуск анимации всплытия
    tmrAni.Interval = 50;
    tmrAni.Start();

}

Туда же можно вставить запуск таймера, который будет анимировать всплытие формы (после base.OnLoad(e)).

Если просто вставить этот код в обработчик события Load, то форма на секунду появится на экране, и будет некрасиво.

3. Далее в обработчиках событий Load и Shown настраиваем нашу форму, например, присваиваем TextBox нужный текст и т.д.

private void frmPopup_Load(object sender, EventArgs e)
{
    //настраиваем TextBox с сообщением
    txtMessage.Height = this.Height - txtMessage.Location.Y - 3;
    txtMessage.Width = this.Width - txtMessage.Location.X - 3;
    txtMessage.BorderStyle = BorderStyle.None;
    txtMessage.BackColor = this.BackColor;
    txtMessage.Text = MessageText;
    txtMessage.ReadOnly = true;
    txtMessage.SelectionStart = 0;

    //и кнопку закрытия
    int CloseX = this.Width - pbClose.Width - 3;
    int CloseY = 3;
    pbClose.Location = new Point(CloseX, CloseY);
}

private void frmPopup_Shown(object sender, EventArgs e)
{
    HideCaret(txtMessage.Handle);
}

3. При каждом срабатывании таймера поднимаем форму на 5 пикселей, а когда форма покажется полностью, то останавливаем таймер и делаем ее «поверх всех окон» (TopMost = true;):

private void tmrAni_Tick(object sender, EventArgs e)
{
    //поднимаем форму на 5 пикселей
    StartPosY -= 5;

    //Если окно видно полностью - останавливаем таймер
    if (StartPosY < Screen.PrimaryScreen.WorkingArea.Height - Height)
    {
        tmrAni.Stop();
        this.TopMost = true;
    }
    else
    {
        SetDesktopLocation(StartPosX, StartPosY);
    }
}

Вот, что получилось:

Код примера целиком

На GitHub

C# Всплывающая форма над областью уведомлений, над NotifyIcon

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

Вот, нашел вам готовый код. Выглядит вполне симпатично.

Можно поиграть с разными опциями, типа эффекта появления и звука при всплытии. Минус — сильно длинный текст отобразить без допиливания не получится.

Потом расскажу, как самому сделать что-то подобное, пусть и не такое симпатичное.

Исходный код

У автора
У меня

C#. Удаление HTML-тегов из текста.

Т.е. остается только текст между тегами, например из:
<b>жирный текст</b> <a href="http://example.org">Это ссылка куда-то</a>
должно получиться
жирный текст Это ссылка куда-то

Регулярное выражение для HTML-тега

По счастью, оно совсем простое:

<[^>]+>

Пример

Не забываем подключить соответствующее пространство имен:
using System.Text.RegularExpressions;
//...
string htmlText = "<html><head><title>tolik-punkoff.com</title></head> <body>Welcome to Tolik Punkoff blog!</body></html>";
OutputText = Regex.Replace(htmlText, "<[^>]+>", string.Empty);

//Содержимое OutputText:
//tolik-punkoff.com Welcome to Tolik Punkoff blog!

Примечание: В примере между фразами пробелы, потому что есть пробелы между тегами. Функция никаких пробелов сама по себе не вставляет!

Калькулятор пропорций

Писал вместе со знакомым школьником, и для его же задания по информатике. А ведь сам давно хотел подобную программу, ибо пропорции последнее время приходится считать часто (химия и проценты всяческие), а с арифметикой я абсолютно не дружу, то и подвигло к освоению компьютера — ему объяснил, как считать, и самому можно не заморачиваться.

Особо каких-то отдельных вещей в программе нет (ввод чисел в TextBox объяснял ранее). Но тем не менее, может еще кому пригодится.

Скриншоты



+3

Исходники

Репозиторий на GitHub

Скачать

Готовую программу
Установщик

C# Ввод отрицательных чисел в TextBox

Преамбула

Продолжаем разговор о вводе чисел в TextBox. Сегодня будем вводить отрицательные числа, тобишь к числу (точнее строке) надо будет пририсовывать знак «минус».

Главное

Раз уж мы будем что-то в TextBox добавлять, то изменится длина строки в TextBox, а, соответственно текстовый курсор (|) перепрыгнет с позиции, на которой он находился, на следующую или предыдущую, что создаст неудобство пользователю. Это надо побороть.

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

int pos = txt.SelectionStart;

Теперь можно вводить знак числа.

Ввод знака числа

Смотрим, какой символ был введен. Если минус, то проверяем, был ли в начале строки минус. Был — убираем, не было — добавляем. В зависимости от того, убрали или добавили символ, корректируем местоположение текстового курсора, добавляя или удаляя позицию, если, соответственно, убрали или добавили символ.

//ввод минуса
if (e.KeyChar == '-')
{                
    if (txt.Text.StartsWith("-"))
    {
        txt.Text = txt.Text.Substring(1);
        txt.SelectionStart = pos - 1;
    }
    else
    {
        txt.Text = "-" + txt.Text;
        txt.SelectionStart = pos + 1;
    }

    e.Handled = true;
    return;
}

Дополнительно. Добавление лидирующего ноля.

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

//ввод точки (запятой)
if ((txt.Text.StartsWith(".")) || (txt.Text.StartsWith(",")))
{
    // добавление лидирующего ноля
    txt.Text = "0" + txt.Text;
    txt.SelectionStart = pos + 1;
}

if ((e.KeyChar == '.') || (e.KeyChar == ','))
{                
    if (txt.Text.Contains(".") || txt.Text.Contains(","))
    {
        e.Handled = true;
        return;
    }                

    return;
}

Вся функция целиком

На PasteBin

Дополнительно

1. Ввод в текстовое поле только цифр Копия
2. Ввод дробных чисел в текстовое поле Копия

Общий пример для всех заметок

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

На GitHub

C#. Ввод в TextBox чисел с дробной частью.

Т.е. нам необходимо ограничить ввод в текстовое поле (TextBox) только цифрами и одной точкой (и/или одной запятой). Далее пример кода (обработчика событий KeyPress), который позволяет вводить цифры и одну точку (или одну запятую).

private void txt_KeyPress(object sender, KeyPressEventArgs e)
{
    //ввод только цифр с одной точкой (запятой)
    if ((e.KeyChar == '.') || (e.KeyChar == ','))
    {
        TextBox txt = (TextBox)sender;
        if (txt.Text.Contains(".") || txt.Text.Contains(","))
        {
            e.Handled = true;
        }
        return;
    }

    if (!(Char.IsDigit(e.KeyChar)))
    {
        if ((e.KeyChar != (char)Keys.Back))
        {
            e.Handled = true;
        }
    }
}

На PasteBin

Дополнительно

Чтобы не заморачиваться, точка у вас или запятая, в строке, содержащей число с дробной частью, можно делать так:

using System.Globalization;
//...

public static double ToDouble(string Number)
{
    Number = Number.Replace(',', '.');
    NumberFormatInfo format = new NumberFormatInfo();
    format.NumberDecimalSeparator = ".";
    return Convert.ToDouble(Number, format);
}

На PasteBin

Класс Convert, к сожалению, зависит от языковых настроек системы, и если в качестве разделителя дробной и целой части в системе указана запятая, а в числе будет точка (или наоборот), то Convert.ToDouble(<число>) свалится с ошибкой.

Всех сопричастных с Днем программиста (и с Пятницей 13)!

Автоматическое получение конфигов и пароля к VPN от vpnbook, теперь и для Windows.

По многочисленным просьбам зрителей, сделал римейк собственных недавних скриптов (копия копия), только теперь для Windows.
Написал небольшую программулину на C#, которая делает то же самое, что и вышеописанные скрипты.
— для распаковки ZIP-архивов использовал библиотеку DotNetZip, она же Ionic.Zip (копия)
— а для распознавания пароля на картинке, не мудрствуя лукаво, вызвал tesseract, естественно, версию под Windows.
Она в архиве с готовыми бинарниками самой программы, единственное, что может потребоваться, это поставить VCRedist для Visual C++ 2015

Скриншоты


Получение пароля

Остальные под катом

Скачать

Программу
— Библиотеки для работы tesseract:
Visual C++ Redistributable for Visual Studio 2015 (c сайта Microsoft)
vc_redist.x86.exe
vc_redist.x64.exe

Исходники

На GitHub

C#. Простой парсинг HTML Regerp’ом.

Преамбула

Да, предваряя камни, которые в меня полетят. Так делать нельзя, неправильно и вообще некузяво. Но что я буду делать, если нормальных парсеров под .NET Framework 2.0 уже net, а задача маленькая — найти все теги <a> или <img> и выдрать из них, соответственно, значение атрибутов href или src.

Решение

public List<string> ParseTags(string Tag, string Property)
{
    List<string> listBuf = new List<string>();

    Regex reHref = new Regex(@"(?inx)
                                <" + Tag + @" \s [^>]*" +
                                    Property + @"\s* = \s*"+
                                        @"(?<q> ['""] )"+
                                            @"(?<url> [^""]+ )"+
                                         @"\k<q>"+
                                 @"[^>]* >");

    foreach (Match match in reHref.Matches(HTMLPage))
    {
        listBuf.Add(match.Groups["url"].ToString());
    }

    return listBuf;
}

Это немного видоизмененное решение (добавил возможность подставлять в регулярное выражение тег и атрибут) из одной статьи на Хабре. Заметка, как обычно, от склероза.

Плюс добавлена функция выбора из найденного, уже без всяких регулярных выражений, с помощью string.Contains(<строка>).

public List<string> Select(string Pattern, List<string> inputList)
{
    List<string> selectList = new List<string>();

    foreach (string s in inputList)
    {
        if (s.Contains(Pattern))
        {
            selectList.Add(s);
        }
    }

    return selectList;
}

C# Windows Forms. Использование ListView для логов, автоматическая прокрутка ListView, избавление от дрожания.

Преамбула

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

В него будем писать лог наших действий. Замутим тестовое приложение:

— Создадим форму с ListView и кнопкой «Начать»
— По нажатию кнопки «Начать» запустим отдельный поток, который будет выводить нам числа от 1 до 100, и генерировать событие.

Подготовка ListView

В коде или в конструкторе установим основные опции для ListView. Нам нужно, чтобы он отображал все, как список. Ну так, как выводится лог в консоль. Устанавливаем соответствующие свойства:

View = Details

Теперь идем в конструктор, ищем опцию Columns, и добавляем единственную колонку:
В появившемся окне все удаляем из поля Text, жмем OK, смотрим на размер (Size) ListWiev, возвращаемся в редактирование колонок, и правим свойство Width. Устанавливаем чуть меньше, чем размер самого ListWiev.

Отладили, посмотрели чтоб было красиво? Ставим

HeaderStyle = None (чтоб не отображался заголовок колонки, заголовки были убраны).

Первый тест

Несмотря на то, что все действия происходят в отдельном потоке, ListView дергается и дрожит:

https://youtu.be/6Un0TSmyw38

На GitHub

Избавляемся от дергания и дрожания

Для этого делаем новый контрол, наследник от ListView и в коде нового класса подправляем параметр отображения:

class MyListView:ListView
{
    public MyListView()
    {            
        this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
    }
}


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

Автоматическая прокрутка ListView

При добавлении нового элемента:

Достаточно при добавлении в ListView устанавливать свойство TopItem в значение последнего элемента. Тогда у ListView появится автоматическая прокрутка:

lvOut.TopItem = lvOut.Items[lvOut.Items.Count - 1];

Результат


https://youtu.be/IzPIf5X8zQQ

Пример

На GitHub