C#, о порядке байт, little-endian, big-endian

Преамбула

Известно, что любые данные хранятся в памяти компьютера в виде последовательности байт, байт обычно равен 8 битам (но бывают и исключения, о которых сейчас не будем). Числа не исключение, например, когда мы определяем переменную int, фактически мы говорим компилятору «выдай нам 4 байта по такому-то адресу в памяти, для записи числа от -2147483648 до 2147483647». Конкретный адрес, конечно, заботливо выбирает для нас среда .NET, и обычно, лезть туда руками не нужно, среда все прекрасно за нас сделает. Пока не случается какой-нибудь хитрый случай, и тут уже приходится думать самому. Когда работаешь с однобайтовыми числами, обычно никаких чудес не происходит — байт он и на десктопной машине с Windows или Linux байт, и на сервере байт, и даже на роутере, телефоне и утюге, скорее всего будет тем же самым байтом. Но все было бы хорошо, если бы одного байта хватало всем 🙂
Как уже сказано выше, тот же int, это не один байт, а 4, и вот тут кроется проблема. Эти самые байты можно хранить в памяти как угодно, хоть через один, хоть в шахматном порядке.
На наше счастье, такие извращения, если и встречаются, то очень редко. В большинстве вычислительных устройств байты (одного числа, например типа int), хранятся либо в последовательности от старшего к младшему big-endian, либо от младшего к старшему little-endian. В случае big-endian первым идет старший разряд числа, а остальные по ранжиру за ним, в случае ltiite-endian — наоборот, первым идет самый младший, последним — самый старший.

Чтобы было проще представить, предположим, что в 1 байт влезает не десятичное число от 0 до 255 (256 значений), а всего 10 значений, т.е. числа от 0 до 9.
В таком случае число 1234 будет «четырехбайтовым», и в случае, если оно записывается в порядке bigendian, то в памяти оно будет храниться в обычном для нас виде, как последовательность 1 2 3 4, поэтому, порядок big-endian еще называют прямым порядком байт. Если же число хранится в порядке little-endian, то в памяти оно будет выглядеть, как последовательность 4 3 2 1, поэтому порядок little-endian называют обратным порядком байт.

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

Итак:
Big-endian — «от старшего к младшему», он же прямой, сетевой (поскольку принят в качестве стандартного порядка байт при передаче данных по сети), Motorola byte order (использовался в процессорах Motorola, а не в честь уехавшего на социальном лифте деятеля).
В big-endian формате хранятся IP-адреса.
Little-endian — «От младшего к старшему», обратный, интеловский (используется в процессорах Intel), VAX (использовался на платформе VAX).

Маленькая иллюстрация

Заведем переменную типа int, содержащую число 16909060 и два массива байт byte[] LittleEndian и byte[] BigEndian, содержащие его представление в виде последовательности байт в обратном и прямом порядке:

int Constant = 16909060;
byte[] LittleEndian = new byte[] {4, 3, 2, 1};
byte[] BigEndian = new byte[] {1, 2, 3, 4 };

Да, число подобрано специально, чтобы было красивое представление его в массиве байт. 🙂

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

BitConverter.ToInt32(LittleEndian,0); //результат - 16909060
BitConverter.ToInt32(BigEndian, 0); //результат - 67305985

Класс BitConverter в теории должен использовать при преобразовании тот порядок байт, который используется на данной машине, соответственно, правильное число 16909060 было получено при преобразовании массива LittleEndian.

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

Определение порядка байт, используемого в системе.

Средствами .NET

Для того, чтобы средствами .NET узнать, какой порядок байт используется на машине, где выполняется ваша программа, можно посмотреть в переменную IsLittleEndian класса BitConverter. Если она принимает значение true, то используется, соответственно, порядок little-endian (обратный):

if (BitConverter.IsLittleEndian)
{
    Console.WriteLine("little-endian");
}
else
{
    Console.WriteLine("most likely big-endian");
}

Самостоятельно

Легко написать функцию проверки самому. Достаточно взять некое заранее известное число, его представление в виде big-endian и little-endian в виде массива байт, сконвертировать массивы обратно в число, и сравнить с заранее известным.

Создадим перечисление ByteOrder(для красоты :):

private enum ByteOrder
{
    BigEndian = 0,
    LittleEndian = 1,
    Unknow = 3
}

И напишем функцию:

ByteOrder DetectBO()
{    
    int Constant = 16909060;
    byte[] LittleEndian = new byte[] { 4, 3, 2, 1 };
    byte[] BigEndian = new byte[] { 1, 2, 3, 4 };

    if (BitConverter.ToInt32(BigEndian, 0) == Constant) 
        return ByteOrder.BigEndian;
    if (BitConverter.ToInt32(LittleEndian, 0) == Constant) 
        return ByteOrder.LittleEndian;
    
    return ByteOrder.Unknow;
}

Примечание: в тексте в кодировке UTF-16 можно определить UTF-16LE или UTF-16BE при помощи BOM, при его наличии

Преобразование из big-endian в little-endian и наоборот.

А вот тут у .NET Framework’а все как-то печально, частично могут помочь статические функции класса System.Net.IPAddress NetworkToHostOrder() и HostToNetworkOrder(), преобразующие, соответственно, число, полученное в big-endian в формат, используемый на данной машине и наоборот, но они довольно ограничены, поддерживают только long, int и short значения, не поддерживают работу с беззнаковыми числами и числами с плавающей запятой, а также с массивами байт. Вот способ преобразования порядка байт:

1. Преобразовать исходное число в массив байт с помощью BitConverter.GetBytes() или взять готовый массив, если он есть.
2. Перевернуть массив функцией Array.Reverse()
3. Преобразовать развернутый массив обратно, с помощью одной из функций класса BitConverter:

byte [] ConvArray = BitConverter.GetBytes(BigEndianValue);
Array.Reverse(ConvArray);
ushort LittleEndianValue = BitConverter.ToUInt16(ConvArray,0);

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

Внимание! Функция Array.Reverse() не создает копии массива, а работает с указанным массивом прямо в памяти. Если в функцию, в которой используется Array.Reverse() будет передан массив из вызывающей подпрограммы, и функция Array.Reverse() будет к массиву применена, то массив изменится. Такое поведение может породить труднообнаруживаемую ошибку, поэтому, если массив в оригинальном виде планируется еще где-либо использовать, то перед Array.Reverse() надо сделать его копию с помощью Array.Copy().

Источники

1. Порядок байтов
2. Разбираемся с прямым и обратным порядком байтов
3. MSDN

Пример к заметке на GitHub

Отключаем SVG в Firefox

Зачем:

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

Как:

Способ для Firefox 53+

1. Набираем в адресной строке about:config
2. Устанавливаем опцию svg.enabled в false

Способ универсальный

Кроме Firefox и в Chrome работает, но тут инструкция для Firefox, для остального должно быть также.

1. Ставим блокировщик рекламы и прочей гадости uBlock Origin
2. Нажимаем на значок плагина (1 на картинке ниже) и входим в панель управления плагином (2 на картинке ниже)

Переключаемся на вкладку Мои фильтры и добавляем туда следующий код:

! block all svg
*.svg

Баста! Зловредный SVG вырезан с корнями!

C# простое преобразование Unix Time в DateTime

Unix-time — способ хранения времени, используемый обычно в POSIX-совместимых системах. Определяется, как количество секунд, прошедших с 01.01.1970 00:00:00, может встречаться в логах, базах данных, и т.д., как отметка времени (timestamp). Следующий алгоритм поможет преобразовать Unix-время в DateTime:

1. Заводим переменную DateTime и инициализируем ее точкой отсчета Unix-time, т.е. полночью 1 января 1970 года:
DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0);
2. Используем метод AddSecounds(), чтобы добавить количество секунд, в качестве аргумента передаем переменную, содержащую Unix-time:
origin.AddSeconds(UnixTime);

Вся функция:

private DateTime UnixTimeToDateTime(double UnixTime)
{
    DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0);
    return origin.AddSeconds(UnixTime);
}

На PasteBin

Если Unix-time хранится в целочисленной переменной, меняем double на нужный тип, компилятор сам преобразует ее в тип double, нужный AddSecounds.

Roadsend php compiler и Roadsend php IDE (бесплатная последняя версия 2.9.0-beta)

Наконец я нашел откомпилированную версию сабжа, и теперь ее можно скачать бесплатно, без СМС и вирусов отсюда. IDE, тащемта, дерьмо, но это можно простить, ибо писалось все это дело в 2008 году, но до сей поры ничего лучшего никто не написал.
Не буду подробно рассказывать, чего мне это стоило, но было нагажено одному болгарскому файл-хостингу и инсталлятор пришлось избавлять от вирей, распространяющих рекламу и майнящих криптовалюту.
Избавили и выкладываем в нормальный открытый доступ. Кто умеет в торренты, помогите и выложите на Рутрекер и TPB, оно маленькое 20 мегабайт всего. Но очень полезное.

Итак: Roadsend PHP Studio (IDE) + Roadsend PHP Compiler
Компилятор PHP для Windows.
Умеет компилировать PHP в EXE в нескольких режимах:
— Console application — на выходе получается стандартное консольное приложение для Win32
— Desktop GUI Application — десктопное оконное приложение, для того, чтоб его собрать нужны еще библиотеки и инструментарий PHP-GTK, в комплекте нет
— Compiled Web Application (FastCGI) — скомпилированное Web-приложение, в экзешник встраивается FastCGI.
— Standalone Web Application (MicroServer) — почти тоже самое, но в экзешник встраивается Web-сервер от Roadsend.
-Library — компилирует полноценный DLL из PHP-кода, который можно встроить в свое приложение, хоть на C#, хоть на C++, хоть на небе, хоть на Аллахе.
Компилировать может в двух режимах — dynamic и static, В статическом режиме в экзешник внедряются DLL от Sheme на котором написан сам компилятор, и их не надо таскать за собой, в динамическом режиме — будет надо, но в статическом вырастает размер готового экзешника, примерно на мегабайт.

Проблемы:
— В Windows 7 не работает штатный лаунчер для IDE (сам компилятор работает).

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

@echo off

set ROADSEND_ROOT=#put you Roadsend root directory here, e.g. C:\roadsend#
set SH=\bin\sh.exe
set IDE=\pcc\bin\loon.exe

if not exist %ROADSEND_ROOT%%SH% goto NOSH
if not exist %ROADSEND_ROOT%%IDE% goto NOIDE

echo Files OK.
echo Launch Roadsend IDE...
%ROADSEND_ROOT%%SH% --login -c "cd /pcc/bin && start loon"


:NOSH
	echo File %ROADSEND_ROOT%%SH% not found
	exit /b 1

:NOIDE
	echo File %ROADSEND_ROOT%%IDE% not found
	exit /b 1

куда вместо #put you Roadsend root directory here, e.g. C:\roadsend# надо вставить директорию, куда установлена IDE с компилятором.

Впрочем, Леша, заглаживая вчерашний демарш, написал свой лаунчер с азартными играми и продажными женщинами на C# и даже с инсталлятором.

Подробнее про Roadsend PHP Compiler и Roadsend PHP Studio можно прочесть в статье на Хабре

Скачать бесплатно, без СМС и вирусов

Оригинальный инсталлятор Roadsend PHP Studio (IDE + компилятор)

Лаунчер для Windows 7 (C#) с Mega.nz
Лаунчер для Windows 7 (C#) с GitHub

Лаунчер для Windows 7 (BAT/CMD) с Mega.nz
Лаунчер для Windows 7 (BAT/CMD) с GitHub

Прочие ссылки

Статья о Roadsend PHP Compiler на Хабр Копия
Исходники лаунчера
BAT/CMD лаунчер на PasteBin

Определение IP и местоположения посетителя сайта 7.

Консольный вариант скрипта.

Т.е. это уже не совсем «определение местоположения посетителя сайта», это просто интерфейс к базе данных Sypex Geo, который можно использовать в консольных приложениях на PHP.

Что переделано:
Переделан изначальный скрипт, для работы под консоль, или, если хотите, а качестве stand-alone скрипта. Для консольного приложения очень важно вернуть код завершения, чтобы далее его могло обработать либо следующая программа в командном файле (BAT/CMD), либо вызвавшая скрипт, как внешний процесс, программа.

Таким же образом был немного подправлен SxGeo.php, распространяемый разработчиками Cypex Geo.

Исправления таковы:

— Везде, где скрипт завершает работу, была добавлена выдача кода завершения
— Таким же образом подправлен класс SxGeo, поставляемый разработчиками Cypex Geo.

Краткая справка скрипта:

Вызов скрипта:
consxgeo.php <ip-address> <database-path> [FIELD_NAME]
consxgeo.php --spec
consxgeo.php --help

--help - Помощь
--spec - Список зарезервированных диапазонов IP-адресов
<ip-address> - Адрес IPv4 (например, 8.8.8.8)
<database-path> - Путь к файлу SxGeoCity.dat
[FIELD_NAME] - Для включения в вывод результата работы скрипта источника IP-адреса. По умолчанию, это значение = MANUAL

Коды завершения:
0 - OK
1 - Предупреждение
3 - Вывод информационных сообщений
4 - Ошибка или неправильные параметры скрипта

Структура ответа:
IP|ISO_CODE|COUNTRY_NAME|CTNR_LAT|CTNR_LON|
REGION_ISO|REGION_NAME|CITY_NAME|CTY_LAT|CTY_LON
|FIELD|MESSAGE|

Пример вывода

Частный IP:


Просто адрес IP:

Сокращенный IP-адрес:

Скрипт на GitHub

Предыдущая часть Копия

PHP. Проверка корректности IP-адреса

Преамбула

[info]ketmar@ljr справедливо заметил, что наши скрипты принимают только IP-адреса в формате xxx.xxx.xxx.xxx, где xxx — десятеричное число от 0 до 255, а IP может быть записан и в виде шестнадцатеричных, и в виде восьмеричных чисел, и даже смешано. Также IP-адрес может быть неполным. Например, адрес 4.0.0.1 можно записать как 4.1
В наших скриптах корректность IP проверялась регулярным выражением, т.к. все равно им же производился поиск IP в полях HTTP_*, в которых IP может передаваться прокси-серверами.
Но валидацию IP можно провести и без использования регулярных выражений. Другое дело, поиск без него уже не произведешь, и это может создать определенные проблемы, если прокси-сервер, например, будет передавать IP в виде шестнадцатеричных чисел или преобразовывать, где возможно, полные адреса в неполные.

Не очень хороший способ валидации IP функцией filter_var()

В PHP 5.2.0 появилась функция filter_var(), которая позволяет проверять строки на соответствие разным форматам, не только IP, но и, например URL или e-mail.
Для проверки IP она вызывается так:

filter_var($ip,FILTER_VALIDATE_IP);
где:
$ip — строка, содержащая IP-адрес
FILTER_VALIDATE_IP — встроенный фильтр для IP-адреса
Функция возвращает TRUE, если строка прошла проверку, и FALSE если нет.

А не очень хорош способ тем, что валидацию проходят только IP, соответствующие формату «четыре десятеричных числа от 0 до 255, разделенных точкой», т.е. работает точно также, как и наша старая функция
Такое ощущение, что разработчики PHP пытаются избавить мир от зоопарка способов записи IP-адреса, и привести все, наконец, к единому знаменателю 🙂

Пример с использованием функции filter_var

Проверка корректности IP-адреса функцией ip2long()

[info]ketmar@ljr предлагал использовать сишную функцию inet_addr(), но где ж я ее возьму-то в PHP, подумал я, и зря подумал. 🙂 Функция ip2long($ip), которая использовалась для проверки попадания IP-адреса в определенный диапазон Копия ее аналог. Если переменная $ip содержит корректный IP-адрес, то функция вернет целое число, если в IP-адресе ошибка, то функция вернет FALSE или -1.

Примечание: Функция ip2long() возвратит FALSE для IP 255.255.255.255 в PHP 5 <= 5.0.2 и -1 в PHP 5 <=5.2.4 на 64-битных системах. Это было исправлено в PHP 5.2.5, теперь возвращается 4294967295. В 32-битных системах будет возвращено -1 из-за целочисленного переполнения.

Так что проверку на корректность IP можно делать в функции-обертке, добавив код-заглушку для IP 255.255.255.255:

function IsIP($ip)
{
	//преобразуем в нижний регистр, на случай шестнадцатиричных чисел
	$ip=strtoupper($ip);
	//ip2long в некоторых версиях php 
	//некорректно реагирует на адрес 255.255.255.255
	//делаем небольшую заглушку
	if ($ip == '255.255.255.255'||$ip == '0xff.0xff.0xff.0xff'||
	   $ip == '0377.0377.0377.0377')
	   {
		   return true;
	   }
	
	$tolong=ip2long($ip);
	
	if ($tolong == -1||$tolong===FALSE) return FALSE;
	else return TRUE;	
}

Преобразование неполных и прочих «необычных» IP-адресов в привычный вид.

В PHP есть функция long2ip(), которая делает обратное преобразование, т.е. преобразует целое число в IP-адрес в привычном виде. Если их совместить, то можно преобразовывать неполные адреса в полные:

function fulladdr($ip)
{
	//преобразует неполные адреса в полные
	//для информации
	$tolong=ip2long($ip);
	return long2ip ($tolong);
}


Пример работы скрипта:
isip.php?ip=4.1

Скрипт на GitHub

Внезапное дополнение

Внедрил последний метод в рабочие скрипты, залил на сервер, и оказалось, что метод не работает. Ну не то, чтобы не совсем не работает, а работает точно так же, как старая функция с регулярным выражением, т.е. возвращает TRUE только если IP-адрес записан в виде четырех десятеричных чисел от 0 до 255, разделенных точкой. Неполные адреса, или адреса в других системах счисления IP-адресами не считаются. Функция ip2long() возвращает FALSE, если ей скормить такой адрес. Уж не знаю, и сходу не нагуглил, отчего такая оказия, то ли такое поведение зависит от настроек PHP, то ли от сервера. На локальном сервере работало, а на том, который в интернете — перестало. Можно сказать, зря делал, но зато сделал валидацию IP без регулярного выражения.

Источники

1. ip2long
2. Ещё раз о filter_var

Определение IP и местоположения посетителя сайта 6.

Нифига не окончание, а второй сезон.

Пока исключительно небольшие исправления.

— Добавили зарезервированные диапазоны IPv4 адресов и соответствующую проверку Копия в скрипты для машинной обработки данных и в красивый скрипт с оформлением, таким образом немножко усовершенствовали скрипт из предыдущей серии.

— Сделали тестовый скрипт, который IP сам ничего не детектит, а просто обращается к SxGeo. Он все равно еще понадобится.

Использование:
onlysxgeo.php?ip=ip-address
Например:
onlysxgeo.php?ip=8.8.8.8
Или:
onlysxgeo.php?ip=ip-address&f=FIELD_NAME
для включения имя поля в ответ скрипта, по умолчанию имя поля MANUAL
Или
onlysxgeo.php?specdiap
Для вывода списка зарезервированных диапазонов адресов IPv4

— По многочисленным просьбам зрителей сделали самый простой скрипт, который просто выводит все IP из заголовков HTTP_* (например HTTP_VIA и/или HTTP_X_FORWARDED_FOR) и REMOTE_ADDR и источник адреса (имя поля HTTP_* или REMOTE_ADDR).

— Завели отдельный репозиторий на GitHub, куда выложили код из всех предыдущих и новых серий

Репозиторий

Запуск php-скрипта в консоли.

Ворчальная преамбула

Дальше будет банальщина, наверняка всем известная, но все же, вдруг кто не знает, да и себе от склероза.

Есть у меня маленький, но очень полезный горшочек скрипт, за которым надо постоянно ходить на сервер, а тут оказалось, что он понадобился на машине, где никаких интернетов нет, а данные, которые нужно обработать — есть. Я сначала решил сделать из него экзешник, но потом оставил эту затею, ибо с компиляторами PHP под Виндовоз неожиданно образовался какой-то жуткий геморрой. HipHop от Facebook напрочь отказался собираться, сайт Roadsend PHP Compiler сдох, а опять собирать с исходников GitHub стало после шаманства с HipHop откровенно влом, ну плюс MinGW особым удобством не отличается.

Заметил, кстати, удивительную странность — на машине с 12 Гб оперативной памяти и четвероядерным процессором, но под MinGW и, соответственно, Виндой, сборка происходит медленнее, чем на компьютере с Linux, скромным двухведерным процессором и двумя же Гб ОП.

Впрочем, все эти танцы с бубном были совершенно лишними.

Запуск скрипта в консоли

Все оказалось банально и элементарно, php прекрасно работает из консоли. Формат команды:

php.exe [параметры] <скрипт> [параметры_скрипта]

Параметры скрипта

Скрипт может «найти» свои параметры в элементах глобального массива $_SERVER:
$_SERVER['argc'] хранит количество параметров
$_SERVER['argv'] их значения. $_SERVER['argv'] сам является индексированным массивом. В элементе 0 содержится имя файла скрипта.

Примечание:
Начиная с PHP 4.3.0, при использовании CLI SAPI переменные $argc и and $argv зарегистрированы и заполнены соответствующими значениями. В более ранних версиях создание этих переменных, так же, как и для CGI или модуля веб-сервера, требует значение on директивы register_globals. Независимо от версии PHP или статуса опции register_global они всегда доступны как элементы массива $_SERVER или $HTTP_SERVER_VARS. Например: $_SERVER[‘argv’]

Пример

Скрипт, выводящий в консоль свои параметры:

<?php	
	echo "Console test \n\n";
	
	echo "Arguments count: ".$_SERVER['argc']."\n";
	echo "Arguments values:\n";
	
	$i=0;
	
	foreach ($_SERVER['argv'] as $arg)
	{
		echo $i.": ".$arg."\n";
		$i++;
	}	
?>

Результат работы

Если запустить скрипт, например так:

php.exe con-test.php param1 param2 param3 tramparamparamparam

Портабельный скрипт

Все это не имело бы большого смысла, если бы для запуска скрипта пришлось бы тащить с собой Web-сервер или весь дистрибьютив PHP, со всеми модулями и прочим. Для минимального запуска PHP под Windows нужно всего 2 файла: php.exe и php*ts.dll, где * — версия PHP. Например, для использованного мной PHP5, это php5ts.dll

Разные мелочи

— Инклюды. Если скрипт подразумевает использование инклюдов, то их лучше держать либо в директории с главным скриптом, либо в поддиректориях, и прописывать в основном скрипте относительные пути. Т.е. так, как это обычно делается на сервере. Иначе PHP будет искать их либо в include_path, который прописан в php.ini, либо вообще неведомо где, точнее ведомо — это указывается как параметр при сборке самого PHP, если мне не изменяет склероз.

php.ini Вообще, для запуска PHP он не нужен, но тут может вкрасться чущественная гадость. Если на машине уже установлен PHP, или php.ini подложен каким-то злоумышленником в каталог по умолчанию, для многих сборок под Windows это %WINDIR%, то может случиться неприятность.
Сам PHP ищет свой php.ini сначала в каталоге с самим собой, так что можно туда его подложить.

— Расширения PHP. Можно подложить нужные DLL расширений в подкаталог в каталоге с php.exe и прописать в extension_dir в php.ini относительный путь к каналогу, а в соответствующих параметрах extension — имена DLL

— PHP я цельностянул из Denwer 🙂

Архив с примером и «портабельным» PHP

Источники

1. Создание EXE приложений на PHP Копия
2. Оффлайновые лекционные тетради в клеточку.

Зарезервированные диапазоны IP-адресов и проверка попадания в них адреса.

Преамбула

В IPv4 имеются зарезервированные диапазоны адресов, в некоторых случаях надо проверить, не попал ли адрес в один из них

Таблица зарезервированных адресов


См. в PDF
Или на PasteBin

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

Вспомогательные функции для проверки

Для порядку заведем регулярное выражение для проверки IP
$ip_pattern="#(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)#";

И функцию, которая проверяет, а IP ли вообще у нас на входе:

function isip($ip_str) //соответствие данных формату IP
{	
	global $ip_pattern;
	$ret=FALSE;
	if (preg_match($ip_pattern,$ip_str)) 
	{
		$ret=TRUE;
	}
	return $ret;
}

Теперь функцию, которая будет проверять, попал ли IP в нужный диапазон:

function chkdiapip ($user_ip, $ip_from, $ip_to) //попадает ли ip в нужный диапазон
{
	return ( ip2long($user_ip)>=ip2long($ip_from) &&
 ip2long($user_ip)<=ip2long($ip_to) );
}

Проверка

Создадим двумерный массив со списком диапазонов:

$spec_list = array(										
	array ("0.0.0.0","0.255.255.255", "Current network"),
	array ("255.255.255.255","255.255.255.255", "Broadcast"),
	array ("255.0.0.0","255.255.255.255", "Reserved by the IETF, broadcast"),
	array ("10.0.0.0","10.255.255.255", "Private network"),
	array ("100.64.0.0","100.127.255.255", "Shared Address Space"),
	array ("127.0.0.0","127.255.255.255", "Loopback"),
	array ("169.254.0.0","169.254.255.255", "Link-local"),
	array ("172.16.0.0","172.31.255.255", "Private network"),
	array ("192.0.0.0","192.0.0.7", "DS-Lite"),
	array ("192.0.0.170","192.0.0.170", "NAT64"),
	array ("192.0.0.171","192.0.0.171", "DNS64"),
	array ("192.0.2.0","192.0.2.255", "Documentation example"),
	array ("192.0.0.0","192.0.0.255", "Reserved by the IETF"),				
	array ("192.88.99.0","192.88.99.255", "Anycast"),
	array ("192.88.99.1","192.88.99.1", "IPv6 to IPv4 Incapsulation"),
	array ("192.168.0.0","192.168.255.255", "Private network"),
	array ("198.51.100.0","198.51.100.255", "Documentation example"),
	array ("198.18.0.0","198.19.255.255", "Test IP"),
	array ("203.0.113.0","203.0.113.255", "Documentation example"),
	array ("224.0.0.0","224.255.255.255", "Multicast"),
	array ("240.0.0.0","240.255.255.255", "Future reserved")					
	);

И функцию, которая будет все это безобразие в цикле перебирать, и если IP попадет в какой-то из диапазонов - выдаст диапазон и описание. Если не попадет - вернет -1.

function get_spec_diap ($user_ip, $listspec)
{
	for ($i=0;$i<sizeof($listspec);$i++)
	{
		$item = $listspec[$i];
		if (chkdiapip($user_ip, $item[0], $item[1]))
		{
			return $item[0]."\t".$item[1]."\t".$item[2]."\t\n";
		}
	}
	
	return -1;
}

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

function printspecdiap ($listspec)
{
	for ($i=0;$i<sizeof($listspec);$i++)
	{
		$item = $listspec[$i];
		echo $item[0]."\t\t".$item[1]."\t\t\t".$item[2], "\t\t\t\n";
	}
}

Скрипт полностью на PasteBin
На GitHub

C# и автозагрузка из реестра.

Преамбула

Оказывается, несколько моих рабочих софтин тихо отвалились, пока пользователи молчали, как партизаны (обычное дело). Теперь я стал работать в Win 7, где не был вырван с корнем UAC и вся виндовая система безопасности. Мне-то хватало Comodo, а что-то действительно важное крутилось на Linux. Ну зато нашел баги. И приобрел опыт.

В чем баг

Я почему-то, если нужна была автозагрузка из реестра, пытался прописываться в HKEY_LOCAL_MACHINE (Software\Microsoft\Windows\CurrentVersion\Run), а для того, чтоб туда прописаться, нужны права администратора.
Чтоб сделать правильно, надо прописываться не в HKEY_LOCAL_MACHINE, а в HKEY_CURRENT_USER, т.е. в куст реестра, ответственный за настройки текущего пользователя, что и вообще правильно, и админских прав не надо.

Как исправил

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

Функции класса

1. Autorun.Add() — добавляет программу в автозагрузку Реестра.
2. Autorun.Remove() — удаляет программу из автозагрузки Реестра.
3. Autorun.Check(bool FixPath) — проверяет статус, если FixPath установлен в True, то исправляет путь к приложению на новый, если приложение было ранее добавлено в автозагрузку, но местоположение экзешника изменилось.
4. Autorun.Switch () — изменяет автозапуск на противоположный (т.е., если программа в автозагрузке — удаляет, если нет — добавляет)

Ссылка на демо

На GitHub

C#, запуск программы от имени администратора

Или как я сделал недо-sudo для Windows 7

Преамбула

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

Поскольку работаю я в основном в консоли, да и рабочий стол захламлять не охота, то от создания кучи ярлыков на нужные батники я отказался. Про штатный runas в Windows я знаю, но он меня тоже не устроил. Хотелось, чтоб как в новом Far Manager’е — при необходимости выполнить операцию от имени администратора, выскакивало стандартное окошко виндового UAC, запрашивалось подтверждение и выполнялась нужная команда.
В общем решил я написать быстренько свой недо-sudo на C#, благо действительно получилось быстро и удобно.

Рассуждения и подготовка

Способ, вполне штатный, произвести такое дело с помощью C# есть, но о нем ближе к концу. Пока скажу, что для запуска внешнего процесса нужен будет полный путь к исполняемому файлу, а прописывать его каждый раз очень сильно влом, потому батники пусть лежат в каталоге, доступном в PATH, а нам придется немного «прикинуться операционкой» и вести себя как она. А операционка действует просто. Если просто введена команда без расширения, то она ищет сначала в текущем каталоге, а потом в каталогах из переменной PATH файл с расширением .com, .exe, .bat, .cmd — нашла, значит выполняет.
Так и будем делать.

1. Заводим два массива, со списком расширений и для хранения списка директорий, в которых будем искать файл:

private static string[] FindDirs = null;
private static string[] Extensions = new string[] {"com","exe","bat","cmd"};

2. Функцию для получения списка каталогов из PATH Копия
3. Функцию, которая будет собирать список каталогов, в которых будет производиться поиск. Не буду здесь ее приводить, она просто добавляет текущую директорию (с помощью Directory.GetCurrentDirectory()) и список, полученный на предыдущем шаге в массив. См. в классе FindApp функцию GetFindDirs()
4. Понадобится функция, определяющая по расширению, является ли файл исполняемым. Она тоже довольно проста. См. ссылку выше private static bool IsExecutable(FileInfo fi). Структуру FileInfo сформируем на шаге поиска далее. Она в цикле сравнивает расширение файла, если оно указано, со списком расширений исполняемых файлов.
5. Функцию, которая ищет файл, если расширение не задано private static string FindNoExt(string path, string FileName) Она проверяет, заканчивается ли путь к директории символом \, в цикле подставляет расширения и проверяет наличие файла по указанному пути с помощью File.Exists(). Если файл найден — возвращает полный путь, если нет — null.

Поиск файла

Функция Find (string Command) в классе FindApp

1. Получаем список каталогов, в которых будем искать, с помощью ранее описанной функции GetFindDirs()
2. Создаем класс FileInfo:

FileInfo fi = new FileInfo(Command);

3. Проверяем, был ли задан путь к исполняемому файлу. Тут надо остановиться немного подробнее. Если в команде Command путь задан не был, то свойство DirectoryName класса FileInfo будет содержать текущий каталог, что никак нам не поможет. Поэтому, надо проверить, был ли задан полный путь. Проще всего это сделать, проверив, содержала ли команда Command символ \:
if (Command.Contains("\\"))
...

4. Если каталог указан, то проверяем, указано ли расширение. Тут можно воспользоваться свойством Extension структуры FileInfo. Оно будет пустым, если расширение не задано. Если расширение задано, то проверяем, является ли файл исполняемым, и в случае, если да — возвращаем его полный путь. Если расширение не указано, пытаемся найти в каталоге исполняемый файл с помощью ранее созданной функции FindNoExt.
5. Если каталог не задан в команде, то выполняем шаг 4 для каждого каталога из списка поиска, пока не найдем файл. Если файл так и не будет найден, значит возвращаем null.

Запуск программы

Выполнение программы организовано в отдельном классе RunApp

Проверка админских прав

Сначала мы должны проверить, а нет ли прав администратора у нас уже. Для этого надо будет подключить пространство имен using System.Security.Principal;
Сама проверка вынесена в отдельную функцию:

public static bool IsAdmin()
{
    WindowsPrincipal pricipal = new WindowsPrincipal(
        WindowsIdentity.GetCurrent());

    return pricipal.IsInRole(
        WindowsBuiltInRole.Administrator);
}


Выполнение

Подключаем пространство имен using System.Diagnostics;

public static bool Excecute(string path, string parameters)
{
    ProcessStartInfo psi = new ProcessStartInfo();
    psi.FileName = path;
    psi.Arguments = parameters;            
    bool admin = IsAdmin();

    if (!admin)
    {
        psi.Verb = "runas";
    }

    try
    {
        Process.Start(psi);
    }
    catch (Exception ex)
    {
        if (admin) //админские права уже были, что-то испортилось
        {
            ErrorMessage = ex.Message;
            return false;
        }
       //иначе может быть просто нажали отмену в UAC
    }
    return true;
}

Сначала формируем структуру ProcessStartInfo из пути к программе и параметров. Далее проверяем наличие админских прав, если их нет, то указываем системе, что процесс-программу нужно запустить с правами администратора с помощью psi.Verb = "runas";
Далее запускаем процесс.
Проверки, правда, минимальные. И наверняка, кривые.

Основная программа

Ну тут вообще все элементарно. Это консольное приложение, при запуске которого ему передаются в командной строке параметры. Первый — имя вызываемой команды, остальные — ее параметры. См. здесь

Источники

Статья на Cyberforum

Скачать

Исходники на GitHub
Готовую программу с GitHub

C# Получение списка каталогов из переменной окружения PATH

Простая задачка, решаемая в две строки.

1. Получаем значение переменной окружения PATH:
string path_var=Environment.GetEnvironmentVariable("PATH");
2. Пути в PATH разделяются символом ; (точка с запятой), соответственно, можно получить массив со списком директорий с помощью функции Split():
string[] dirs = path_var.Split(';');

Единственное что нужно не забыть перед использованием — последний элемент массива может оказаться пустым, если PATH заканчивается точкой с запятой, и между элементами в переменной PATH могут быть пробелы, надо не забыть сделать Trim()перед использованием значения из массива.

C#, динамическая NotifyIcon с наложением изображения.

Спрашивают, а возможно ли совместить пример с динамически обновляемой NotifyIcon Копия с примером про наложение изображения Копия, чтобы получить что-то типа такого же эффекта, который делает иконка сетевого соединения в Windows 7 при разрыве соединения?

Можно, только, на мой взгляд, в Windows это все-таки сделано просто выводом разных иконок в случае отсутствия или присутствия соединения с сетью, просто в первом случае к иконке пририсован восклицательный знак. Вообще динамической генерацией иконок злоупотреблять не стоит, если уж делать, то делать там, где это действительно нужно. Надо помнить, что для корректной работы с динамически генерируемыми иконками, приходится тащить за собой функцию WinAPI, хоть и всего в одном месте.

Но если очень хочется, то можно.

Для разнообразия будем считать, что основное изображение у нас уже в формате ICO:

А накладываемое должно быть PNG с прозрачностью. Накладывать будем желтый треугольник с красным восклицательным знаком:

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

— Заводим необходимые переменные:

Icon TargetIcon = Properties.Resources.target;
Bitmap TargetBitmap = null;
Bitmap ResultBitmap = null;
Bitmap OverlayBitmap = null;
IconConverter icconv = null;

И не забываем экспортировать функцию DestroyIcon из user32.dll [1]:

[System.Runtime.InteropServices.DllImport("user32.dll",
            CharSet = System.Runtime.InteropServices.CharSet.Auto)]
extern static bool DestroyIcon(IntPtr handle);

— Конвертируем иконку TargetIcon в Bitmap. Делается это при помощи класса IconConverter:

//convert icon to bitmap (target)
icconv = new IconConverter();
TargetBitmap = (Bitmap)icconv.ConvertTo(TargetIcon, typeof(Bitmap));

— Достаем из ресурсов накладываемое изображение и далее поступаем, как в [2]:

//overlay bitmap
OverlayBitmap = Properties.Resources.overlay;

//create result bitmap
ResultBitmap = new Bitmap(TargetBitmap.Width, TargetBitmap.Height,
    PixelFormat.Format32bppArgb);

//create Graphics
Graphics graph = Graphics.FromImage(ResultBitmap);
graph.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;

//Draw overlay images
graph.DrawImage(TargetBitmap, 0, 0);
graph.DrawImage(OverlayBitmap, 0, TargetBitmap.Height-OverlayBitmap.Height);            

//result =)
pbTarget.Image = TargetBitmap;
pbOverlay.Image = OverlayBitmap;
pbResult.Image = ResultBitmap;

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

IntPtr hIcon = ResultBitmap.GetHicon();
System.Drawing.Icon niicon = System.Drawing.Icon.FromHandle(hIcon);
niTest.Icon = niicon;

DestroyIcon(niicon.Handle);

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

Источники

1. C#, динамическая NotifyIcon, иконка в области уведомления Копия
2. C#, WindowsForms, наложение изображений, изменение размеров изображения. Копия

Код примера на GitHub

Виндоофисные мелочи.

Заметка от склероза.

Завершение работы с командной строки

Команда shutdown.
Завершение работы:
C:\Windows\System32\shutdown.exe /s /t 10 /c "I am shutdown"
Перезагрузка:
C:\Windows\System32\shutdown.exe /r /t 10 /c "I am reboot"
Отмена:
C:\Windows\System32\shutdown.exe /a
Архив с готовыми ярлыками, чтоб кидать сразу на рабочий стол
Справка по команде

Батник для очистки EXIF в фотографиях

Где-нибудь в PATH должен лежать exiftool.exe
Батник из одной строки:
exiftool -all= -overwrite_original %1
Использовать: killexif.bat *.jpg
Ссылка на SFX-архив с exiftool и батником

Посадить макрос на сочетание клавиш в Word 2003

1. Идем в Сервис —> Настройка
2. Жмем кнопку Клавиатура в появившемся окне:

3. В Категории выбираем Макросы
4. В соседнем списке выбираем нужный макрос
5. Жамкаем на поле Новое сочетание клавиш
6. Нажимаем что нужно
7. Все сохраняем

C#, WindowsForms, наложение изображений, изменение размеров изображения.

Задали вопрос, а можно ли в C# программными средствами наложить 2 изображения друг на друга. И даже сами потом предложили какое-то жуткое решение с привлечением WinAPI, и чуть ли не ассемблера со злыми духами.

На самом деле, задача вполне себе решается стандартными средствами.

Наложение изображений

Итак, предположим, что у нас есть 2 изображения, оба они PNG с прозрачностью, и лежат в ресурсах нашего приложения. Например, флаг:

и герб:

под именами, соответственно Properties.Resources.flag и Properties.Resources.trizub_small

Сначала сделаем из изображений два объекта Bitmap:

//Берем целевое изображение
Bitmap TargetBitmap = Properties.Resources.flag;

//Берем накладываемое изображение
Bitmap OverlayBitmap = Properties.Resources.trizub_small;

Теперь надо создать результирующее изображение (оно будет пока пустым) нужного размера:

//Создаем результирующее изображение (пока пустое)
Bitmap ResultBitmap = new Bitmap(TargetBitmap.Width, TargetBitmap.Height,
            PixelFormat.Format32bppArgb);

Откуда взяли высоту и ширину — понятно, третий параметр PixelFormat берется в зависимости от исходных изображений. Желательно, чтоб они совпадали по глубине цвета, иначе получится некрасиво, может потеряться прозрачность или произойти еще какая-нибудь бяка. Я сделал 2 изображения с прозрачностью (ARGB) и глубиной цвета 32 бита.

Теперь нужно создать объект Graphics, который и будет заниматься совмещением изображений. Раз мы будем рисовать в пустом Bitmap ResultBitmap, то и объект Graphics создаем из него, воспользовавшись методом Graphics.FromImage():

//Создаем объект Graphics из результирующего изображения
Graphics graph = Graphics.FromImage(ResultBitmap);

Далее объект graph надо настроить:

//настраиваем метод совмещения изображений
graph.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;

Если вместо SourceOver установить SourceCopy, то потеряется вся прозрачность у накладываемого изображения, и будет некрасиво:

Далее, производим отрисовку:

//рисуем основное изображение
graph.DrawImage(TargetBitmap, 0, 0);

//рисуем накладываемое изображение
graph.DrawImage(OverlayBitmap, (TargetBitmap.Width-OverlayBitmap.Width)/2,
(TargetBitmap.Height-OverlayBitmap.Height)/2,
OverlayBitmap.Width,OverlayBitmap.Height);

Думаю, откуда взяты все координаты и размеры, понятно.

Осталось только присвоить Bitmap'ы PictureBox'ам

Изменение размеров изображения

Тут тоже ничего сложного и сверхъестественного нет.

Чтоб два раза не вставать, возьмем полученное выше изображение и уменьшим его:

//задаем новые размеры
int NewWidth = ResultBitmap.Width / 2;
int NewHeight = ResultBitmap.Height / 2;
//Настраиваем PictureBox для вывода уменьшенного изображения
pbResize.Size = new Size(NewWidth, NewHeight);

Создадим новый Bitmap для будущего уменьшенного изображения:

//создаем новый Bitmap для измененного изображения
Bitmap ResizeBitmap = new Bitmap(NewWidth,
NewHeight);

Опять создадим объект Graphics, который будет заниматься отрисовкой:

//создаем объект Graphics, который будет изменять размер
Graphics ResizeGraph = Graphics.FromImage(ResizeBitmap);

Поставим повыше качество изображения:

//ставим высокое качество
ResizeGraph.InterpolationMode =
System.Drawing.Drawing2D.InterpolationMode.High;

И делаем отрисовку:

//рисуем изображение с измененным размером
ResizeGraph.DrawImage(ResultBitmap, 0, 0, NewWidth, NewHeight);

В заметке я пропустил вывод изображений в PictureBox'ы, но он и так очевиден (в исходнике есть).

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

Исходник примера на GitHub

C#, WindowsForms. Автоматическая выгрузка и загрузка содержимого контролов на форме. В объект или запись DataSet.

Решил побороть еще одно узкое место в коде, которое приводит к невероятному количеству ручного кодинга, а именно — загрузка данных из записи DataSet или произвольного объекта в форму, для изменения/ввода данных пользователем, а потом обратная выгрузка.

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

Оказывается, можно. Все инструменты для этого есть — есть System.Reflection, через инструменты данного пространства имен можно получать имена свойств или полей нужного класса, а также тип данных, и есть методы для поиска нужных контролов на форме, чтобы загрузить или сохранить данные в/из объекта.

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

Опять же, тут будет краткое рассуждение, а пример кода в конце.

Основные компоненты

Основные компоненты это:
— текстовые поля, куда можно ввести либо строку, либо число (фильтрацию ввода оставим форме)
— checkbox’ы, хранящие булево значение
— radiobutton’ы/переключатели — ограниченный выбор из определенного набора вариантов.

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

Как хранить набор вариантов

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

public enum NetConnectionType
{
    NoProxy = 0,
    SystemProxy = 1,
    ManualProxy = 2
}

Соглашение об именах

Это единственный наглый момент во всем примере — контролы придется называть не абы как, а по правилам, впрочем, правила всегда можно переопределить, и сделать удобные вам. Я делал удобно для себя.
Для TextBox‘ов и CheckBox‘ов правила такие: сначала идет префикс txt или chk, далее — название поля в записи таблицы DataSet или название свойства объекта, например chkAutorun или txtUserName. В классе, соответственно, должны быть поля bool Autorun или string UserName.

Для радиокнопок (переключателей) имя формируется по следующему принципу: префикс rb+НазваниеСвойства+ЗначениеВEnum, т.е., например, радиокнопка, указывающая на прямое соединение, будет называться rbConnectionNoProxy

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

Разные вспомогательные мелочи

Получение списка радиокнопок и поиск контрола на форме Копия

Получение значения Enum из состояния RadioButton:

private string GetEnumValFromRb(string PropName)
{
    string EnumVal = string.Empty;
    string rbName = "rb"+PropName;            
    
    foreach (RadioButton rb in RadioButtons)
    {                
        if (rb.Checked)
        {
            if (rb.Name.StartsWith(rbName))
            {
                EnumVal = rb.Name.Substring(rbName.Length);
            }
        }
    }

    return EnumVal;
}

Весь код примера

На GitHub

C#, WindowsForms — найти все переключатели (RadioButtons) на форме.

Простой поиск контрола

Если нам нужно найти контрол на форме, зная его имя, то все решается довольно просто — у массива контролов Controls есть метод Find, который найдет нам что нужно, если указать правильное имя контрола:

private Control FindControl(string ControlName, Form form)
{
    Control ctrl = null;

    Control[] buf = form.Controls.Find(ControlName, true);
    if (buf.Length == 0) return null;
    if (buf.Length > 1) return null;

    ctrl = buf[0];

    return ctrl;
}

Поиск всех RadioButton’s (или других однотипных контролов)

Вот тут уже сложнее, особенно с переключателями. Они обычно сидят на форме в контейнерах, например в GroupBox'ах, и функция Find тут не поможет. Необходим другой подход, если мы хотим получить список контролов определенного типа. А именно — надо сделать рекурсивную функцию поиска. Кто боится рекурсии и связанных с ней переполнений, скажу, что ничего страшного нет.
Мне удалось уронить студию на 5 000 однотипных компонентов, а подобное число компонентов вряд ли может быть в реальности, только если вы не радиокнопочный маньяк 🙂

Функция такая вот:

private List FindAllRadiobuttons(Control.ControlCollection collection)
{
    List  result = new List();
    foreach (Control ctrl in collection)
    {
        if (ctrl.HasChildren)
        {
            result.AddRange(this.FindAllRadiobuttons(ctrl.Controls));
        }
        if (ctrl is RadioButton)
        {
            result.Add((RadioButton)ctrl);
        }
    }

    return result;
}

Т.е. если мы просто наткнулись на переключатель, при переборе контролов из массива Controls, то добавляем переключатель в массив, если же, мы наткнулись на контрол-контейнер (ctrl.HasChildren == true), то вызываем функцию перебора массива уже для массива Controls контейнера.

C#. О конфигах и сохранении/загрузке свойств объекта, часть 2

В прошлой части Копия я рассказывал, как с помощью инструментов из пространства имен System.Reflection, можно сохранить свойства объекта, например, в таблицу DataSet, или наоборот, загрузить из DataSet данные в свойства соответствующего объекта. Таким образом, решалась проблема автоматизации работы с конфигурационными файлами.

Есть способ еще более уменьшить количество кода, воспользовавшись стандартным механизмом .NET Framework — сериализацией. Сериализация, это, по рабоче-крестьянски говоря, именно что сохранение состояния объекта (он же пафосно называется «экземпляром класса») в некий передаваемый формат. Доскональное объяснение, что это такое, в статью не влезет, потому оставим.

Итак, переходим к сериализации.

В .NET Framework сам себя класс сериализовать не может, точнее, сериализовать-то может, а вот десериализовать — нифига. Класс и его экземпляр, получается, как Штирлиц с раненой радисткой Кэт, передать могут, а обратно нет, без дружественной помощи.

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

Итак, создадим класс-хранилище, вот такой вот, например:

[Serializable]
public class AppSettings
{
    public string DataUrl { get; set; }
    public FormatType DataFormat { get; set; }
    public string IPColumn { get; set; }
    public string FieldSeparator { get; set; }
    public string FlagColumn { get; set; }
    public string TrueValue { get; set; }
    public string FalseValue { get; set; }
    public bool LoadUpdate { get; set; }

    public AppSettings()
    {
    }
}

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

[Serializable]

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

Ну, раз уж в прошлый раз, мы выбирали XML, то и сейчас я буду сериализовывать класс в XML

Особенности сериализации в XML в .NET Framework

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

Если какое-то публичное свойство не надо сериализировать, т.е. в нашем случае, сохранять в конфиг, то такому свойству надо установить атрибут [XmlIgnore], для публичных полей устанавливается атрибут [NonSerialized].

Например:

[XmlIgnore]
public string DataUrl { get; set; }

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

Класс-менеджер

Само дерево жужжать не может
Значит, кто-то тут жужжит
(Вини-Пух)

Это самая гадская особенность сериализации в .NET, а именно, если в прошлом случае могли параметры конфига, и функции для их сохранения-загрузки объединить в один класс, то в подходе с использованием сериализации не можем:

this = (Data)readerRr.Deserialize(fileRr);
this - переменная только для чтения, по крайней мере до .NET 4.0 включительно.

И такой подход считается «плохим дизайном», хотя на мой нескромный взгляд, плохой дизайн — это разносить части одного и того же по разным классам.

Но раз уж надо, значит надо. Делаем класс-менеджер:

Далее такой условный класс-менеджер с возможностью сохранения и загрузки:
Читать далее

Исходник примера на GitHub

AppSettings.cs

Источники

Киберфорум
Сериализация в XML. XmlSerializer

Навел на мысль [info]steinkrauz@ljr

DetecTOR, утилита, определяющая, относится ли IP к сети TOR

Написана изначально была аж в 2013 году и довольно кривовато, но по многочисленным просьбам нашего дорогого зрителя, была переделана, с подробными объяснениями по поводу «как», «что», «где» и «куда».

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

Выглядит главное окно вот так:

readme.txt

Чтоб два раза не вставать.
readme.txt

Смотреть исходники (на GitHub)
Скачать программу (Win32) c GitHub