Отложенная автозагрузка.

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

Возникла тут производственная задача. Некоторые программы, необходимые для работы, находятся на сетевых шарах (либо подключаемых/шифрованных контейнерах), однако, после загрузки рабочей ОС, они должны автоматически запускаться у пользователя. Для примера, на работе это была программа управления станком.

Решая задачу, перелопатил гору литературы, но ничего готового не нашел. В результате написал свой «Менеджер отложенной автозагрузки».

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

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

В качестве запускаемой задачи можно указать не исполняемый файл (exe, com, bat, cmd), но и документ, тогда он откроется в ассоциированной с ним программе.

Для исполнения задач, необходимо запустить менеджер с параметром командной строки /run

Скриншоты


Основное окно настроек

Опции «Менеджера отложенной автозагрузки»

Окно добавления/редактирования задачи

Cправка по параметрам командной строки

StartupDelayed /help
StartupDelayed [/run] [/d|confdir <путь>]
/help
— эта справка
/run — выполнение задач
/d — запускать в «не-портативном режиме» (конфигурационные файлы в директории %LocalApplicationsData%\StartupDelayed)
/confdir <путь> — указать путь к директории с файлами конфигурации

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

Скачать

Инсталлятор (не портативная версия)
Портативная версия
Исходники на GitHub

DetecTOR v 0.3.0b.

Обновлена утилита DetecTOR, которая определяла, присутствует ли определенный IP в сети Tor.

По многочисленным просьбам зрителей добавлен:

— модуль SxGeoSharp, теперь, даже если IP отсутствует в сети Tor, программа возьмет информацию из базы SxGeo (если вы ее скачаете и подгрузите, она бесплатная), и выдаст вам страну (+город и регион, если есть) для конкретного IP-адреса.
— по умолчанию включен портабельный режим, все настройки программы хранятся в подкаталогах с исполняемым файлом.
— режим пакетной обработки. Если у вас есть огромный лог или просто список IPv4 адресов, (тестировалось на 1000 IP-адресов в специально нагенерированном текстовом файле, где IP были расположены в случайном порядке), то программа найдет все адреса, проверит их по БД SxGeo и БД адресов Tor и выдаст результат в виде файла CSV. Можно включить или отключить выдачу в отчет дополнительных данных Tor.
— добавлены дополнительные аргументы командной строки (см. readme.txt).
— проверено замечание пользователя [info]paperdaemon@ljr. Данная ошибка при работе в Windows 7 и более ранних версиях так и не была выявлена.

readme.txt

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

C#, DataSet, пользовательские типы данных, и хинтик при использовании Dataset Designer.

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

public enum testenum
{
     val0=0,
     val1=1
}

и подключим какое-нибудь дополнительное пространство имен, например:

using System.Diagnostics;

Теперь, в таблицу DataSet можно добавить поля типов testenum и, например, ProcessWindowStyle (из System.Diagnostics)

//...
DataSet dsTest = new DataSet();
//...
dsTest.Tables.Add("Test");
dsTest.Tables["Test"].Columns.Add("Text", typeof(string));
dsTest.Tables["Test"].Columns.Add("Enum", typeof(testenum));
dsTest.Tables["Test"].Columns.Add("Enum2", typeof(ProcessWindowStyle));

Код на PasteBin

Если же делать типизированный DataSet, т.е. добавить в проект DataSet, как отдельный класс (наследник обычного DataSet), и создать нужные таблицы в конструкторе (Dataset Designer), то при попытке просто прописать пользовательский тип DataType в конструкторе, получится ошибка:

На самом деле, имена типов данных нужно вводить полностью, вместе с их пространствами имен. Т.е., при условии, что пространство имен программы, например tmpDataSet, то тип testenum нужно указывать как tmpDataSet.testenum (а тип ProcessWindowStyle, соответственно, как System.Diagnostics.ProcessWindowStyle)


Вещь, вроде бы довольно очевидная, если приглядеться (стандартные типы из списка прописываются точно также):

Но почему-то прямо нигде не озвученная, что странно.

C#, Регулярное выражение для IP-адреса (v4)

Искать айпишники, например, в логах.
Для десятичной (полной) записи:

(25[0-5]|2[0-4]\d|[01]?\d\d?)(\.(25[0-5]|2[0-4]\d|[01]?\d\d?)){3}

Второе, должно поддерживать восьмеричную, шестнадцатеричную, десятичную и смешанную запись:

(0[0-7]{10,11}|0(x|X)[0-9a-fA-F]{8}|(\b4\d{8}[0-5]\b|\b[1-3]?\d{8}\d?\b)|((2[0-5][0-5]|1\d{2}|[1-9]\d?)|(0(x|X)[0-9a-fA-F]{2})|(0[0-7]{3}))(\.((2[0-5][0-5]|1\d{2}|\d\d?)|(0(x|X)[0-9a-fA-F]{2})|(0[0-7]{3}))){3})

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

Протестировать можно здесь
Ну и тесты на C# (от Лехи)

C#, Об анализе exceptions при вызове внешнего процесса.

Или отлавливаем нажатие клавиши «Отмена» в окне запроса UAC.

Вот однажды я писал небольшую утилиту, которая запускает любое приложение или командный файл (bat/cmd) от имени администратора. И мне в комментариях правильно намекнули, что я слишком грубо обрабатываю exceptions, которые могут случиться во время запуска внешнего процесса. Но вообще это хороший пример не только для конкретного случая, но и для подхода к обработке ошибок вообще. Кратко говоря — если вы предполагаете, что где-то может возникнуть ошибка, то есть два метода:
1. Предотвратить и обезвредить. К таким ошибкам, например, относится возможная недоступность файла для чтения/записи, или вообще его отсутствие, когда он нужен. Тогда лучше проверить, например, наличие файла, с помощью File.Exist() перед операцией с файлом.
2. Отловить на этапе времени выполнения. Для этого в C# существуют try/catch.

Нам нужен именно способ #2, поскольку мы не знаем и проверить заранее никак не можем, нажмет пользователь «Отмену» в окне запроса, или нет.

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

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

Немного теории об exceptions при запуске внешних процессов.

Как известно, в .NET ошибки времени выполнения распределены по классам. Есть общий класс — Exception, в который попадают все ошибки времени выполнения, и есть конкретные классы для обработки определенных ошибок. Иерархия обработки следующая: «от конкретных к общему». Т.е. сначала (если мы хотим их обработать), указываются конкретные ошибки, а потом можно, но не обязательно указать общий обработчик.
Конкретно при запуске внешних процессов могут возникнуть следующие виды ошибок:
ArgumentNullException
ObjectDisposedException
FileNotFoundException
(на самом деле в зависимости от OS но может не сработать, сработает следующий)
Win32Exception
он нам и нужен
PlatformNotSupportedException

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

Анализ Win32Exception

Для начала подключим нужный namespace:
using System.ComponentModel;

А теперь примемся за анализ. На самом деле, у каждой Win32-ошибки имеется внутренний код. В C#-exceptions он сохраняется в переменной NativeErrorCode. Т.е для решения нашей задачи, нам в конструкции try/catch достаточно отловить конкретный код ошибки. Для нажатия клавиши «Отмена» в окне UAC, это будет код 1223, «Операция отменена пользователем».

Исправление.

1. Сначала надо отловить ошибку типа Win32Exception и проанализировать значение NativeErrorCode.
2. Если NativeErrorCode == 1223, то не предпринимаем никаких действий.
3. Если NativeErrorCode другой, оповещаем пользователя об ошибке.
4. Если сработало исключение другого типа (не Win32Exception), то аналогично предыдущему пункту — оповещаем пользователя об ошибке.

//...

try
{
    Process.Start(psi);
}
catch (Win32Exception wex)
{
    if (wex.NativeErrorCode == 1223) //нажали "Отмену" в окне UAC
    {
        return true;
    }
    else //какой-то другой Win32 Error
    {
        ErrorMessage = wex.NativeErrorCode.ToString() + " " + wex.Message;
        return false;
    }
}
catch (Exception ex) //какой-то другой Exception
{
    ErrorMessage = ex.Message;
    return false;
}

//...

Код полностью здесь

Источники

1. Коды ошибок Win32 (Краткое пояснение и полный список кодов, англ., MSDN)
2. Нужный код ошибки (Отменено пользователем)

Утилита wsudo

1. Репозиторий на GitHub
2. Скачать
3. Заметка об утилите Копия

C#, программное создание ярлыка (shortcut) Windows

И случайно, самое полное описание WshShortcut.

Преамбула

Почему-то, уж не знаю почему, в .NET Framework (во всяком случае до 4 версии, в 4 вроде появился) не было стандартного способа создать ярлык (файл .LNK) программно. Но способы все-таки есть. Расскажу о них, в порядке уменьшения геморройности.

I. Создать ярлык вручную

Самый геморройный способ, для любителей ассемблера и прочего байтокопательства. Файл ярлыка (*.lnk), это обычный бинарный файл. Почему-то в сети бытует мнение, что формат LNK-файлов закрыт, и чуть ли не засекречен. Однако это не так, спецификация формата вполне себе открыта и лежит на официальном сайте Microsoft. Так что остается осилить 48 страничную спецификацию, и можно приступать. 🙂 Но мы этого делать не будем. Замечу лишь, что в формате файла есть несколько странных моментов. Например, зачем хранить в файле ярлыка серийный номер тома и тип диска (HDD, CD, Floppy) и NetBIOS имя компьютера, я совершенно не понимаю.

II. Обратиться к Windows Script Host через COM-интерфейс

Способ, наиболее часто встречающийся в сети, но почему-то, весьма поверхностно описанный. Windows Script Host — компонент Microsoft Windows, предназначенный для запуска сценариев на скриптовых языках JScript и VBScript, а также и на других дополнительно устанавливаемых языках (например, Perl).

Остановлюсь поподробнее на некоторых моментах. Сначала самое основное.
В References проекта надо добавить соответствующий компонент (щелкнуть по References правой кнопкой мыши, выбрать Add Reference…) В появившемся окне выбираем вкладку COM и находим компонент Windows Script Host Object Model.

Из-за того, что мы используем COM-интерфейс, с нашей программой придется таскать библиотеку для взаимодействия с ним Interop.IWshRuntimeLibrary.dll (ее нам без нашего участия сделает компилятор .NET).
Теперь указываем соответствующую директиву using:

using IWshRuntimeLibrary;

Создаем объект WSH Shell:

WshShell wshShell = new WshShell(); //создаем объект wsh shell
На самом деле у объекта WshShell довольно много интересных возможностей, например выполнять VBS или JS сценарии прямо из кода C#, со всеми возможностями Windows Scripting Host, естественно. Но это так, к слову. Мы же создадим объект для управления ярлыком:

IWshShortcut Shortcut = (IWshShortcut)wshShell.
CreateShortcut(ShortcutPath);

где ShortcutPath — строковая переменная, в которую записан путь к файлу создаваемого ярлыка.

В самом простейшем случае, надо задать имя файла для которого создается ярлык:

Shortcut.TargetPath = @"C:\Windows\notepad.exe"; //путь к целевому файлу

Если дополнительные параметры не заданы, то:
— в качестве иконки ярлыка будет установлена иконка по умолчанию (для EXE — его иконка, для остальных — стандартные системные иконки)
— в качестве рабочего каталога — каталог, в котором расположен целевой файл (тут C:\Windows\).
— размер окна — нормальный.

Теперь нужно сохранить ярлык:
Shortcut.Save();

Пример кода целиком на PasteBin

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

Пример кода функции, задающей дополнительные параметры ярлыка на PasteBin

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

Горячая клавиша:
Задается параметром string Shortcut.Hotkey
Внимание! Если переменной попытаться установить значение null, произойдет ошибка нехватки памяти (OutOfMemoryException). Любая строка не подпадающая под формат, вызывает ArgumentException "Значение не попадает в ожидаемый диапазон."

Строка должна быть следующего вида: "Ctrl+Alt+N", т.е. содержать названия клавиш-модификаторов, символьную или функциональную клавишу, названия должны быть разделены знаком + без пробелов. Названия регистронезависимы.
Чтобы хоткей сработал, ярлык надо создавать или в меню Пуск, или на Рабочем столе. Почему-то если создать ярлык где-то еще, а потом скопировать в Пуск или на Рабочий стол, хоткей не работает (хотя, если менять горячую клавишу у уже созданного ярлыка, через свойства ярлыка, то все работает). Установленный хоткей становится глобальным для всей системы, т.е. если "Ctrl+Alt+N" обрабатывается в какой-то программе, то после создания ярлыка, сочетание клавиш будет перехвачено Windows, и запустится то, на что указывает ярлык.
Список возможных клавиш:
Модификаторы: CTRL+ ALT+ SHIFT+ (и еще какой-то EXT+ встречается в [1])
Алфавитно-цифровые, функциональные и прочие:
F1-F12, 0-9, A-Z
ESC, ENTER, TAB, SPACE, PRINT SCREEN
(указывается как SNAPSHOT), BACKSPACE [1] (причем обычным способом через проводник установить их нельзя, и нет, Ctrl+Alt+Del так не перехватить, хотя создать такой хоткей можно).
Полный список клавиш можно посмотреть в WINUSER.H или в [2], имена берутся без VK_, и не получится в качестве третьей клавиши использовать имена модификаторов и мышиных кнопок, ярлык создастся без ошибок, а вот работать не будет.

Забавный баг

Через свойства ярлыка нельзя задать горячую клавишу БЕЗ модификаторов. Windows заботливо будет нам подставлять CTRL+ALT+, а вот с помощью WshShortcut — можно, т.е. если значение Hotkey установить, например в «F1" и создать ярлык на Рабочем столе, то по нажатию F1 будет вызываться, например, Блокнот. На практике это использовать, конечно, никак нельзя, разве что над кем-нибудь подшутить.

Демо и класс-обертка над IWshShortcut на GitHub

III. Создание ярлыка через Windows API.

Вообще, этот способ по геморройности надо было бы ставить на второе место, чего одна статья [3], описывающая все API, стоит. Но авторы статьи, крутые акулы программирования, для нас постарались и таки сделали классы для работы с ярлыками, причем сделали великолепно! С помощью их класса ShellLink можно не только создавать новые ярлыки, но и читать/редактировать существующие.
ShellLink вместе с демо можно скачать с mega.nz или с моего репозитория на GitHub, не знаю, будут ли проблемы с лицензией, но на vbaccelerator.com вроде Creative Commons.
С официального сайта почему-то сей полезный горшочек пропал, хотя статья осталась. Видать авторы забили на проект и что-то протухло.
Повторюсь, класс написан хорошо, ничего не падает и с ошибками не вылетает. Единственное, что криво, это установка хоткея. Ну да и черт с ним, мне особо не нужно было, так что ковыряться и исправлять не стал.
Написано сие дело аж в 2003 г., но прекрасно работает до сих пор, на Windows 7 в т.ч.

Для подключения к своему проекту из оригинального архива понадобятся два класса (файлы ShellLink.cs и FileIcon.cs), далее подключаем соответствующий namespace (using vbAccelerator.Components.Shell;) и можно использовать. Пример кода:

ShellLink shortcut = new ShellLink();
shortcut.ShortCutFile = @"C:\Temp\shortcut\test.lnk";
shortcut.Target = @"C:\Windows\notepad.exe";
shortcut.WorkingDirectory = @"C:\";
shortcut.IconPath = @"C:\Windows\System32\shell32.dll";
shortcut.IconIndex=111;
shortcut.Description = "Тестовый ярлык";
shortcut.Arguments = "file.txt";
shortcut.DisplayMode = ShellLink.LinkDisplayMode.edmMaximized;
shortcut.Save();

Источники

1. WshShortcut.Hotkey Копия
2. Virtual-Key Codes Копия
3. Creating and Modifying Shortcuts (ShellLink, WebArchive) Копия
4. Создание ярлыков с помощью Windows Script Host
5. Спецификация формата файлов LNK Копия

Исходный код

1. ShellLink от vbaccelerator. Скачать с Mega.NZ. На GitHub
2. Демо и класс-обертка над IWshShortcut на GitHub

C# Получение пути к папке ОС Windows

Оказывается, вплоть до версии .NET 4 в перечислении Environment.SpecialFolder нет пути к папке, куда установлена Windows (обычно C:\Windows). Эту досадную оплошность можно обойти двумя способами:
1. Посмотреть в переменную окружения SystemRoot или windir:
string windir=Environment.GetEnvironmentVariable("SystemRoot");
string windir=Environment.GetEnvironmentVariable("windir");

2. ВНЕЗАПНО, бывают хитрые самосборные или специальные дистрибутивы, где данных переменных окружения нет. Тогда:
— надо взять путь к директории system, (обычно это C:\Windows\System32), который есть в Environment.SpecialFolder во всех версиях .NET: Environment.SpecialFolder.System
— получить директорию выше уровнем:

string windir = System.IO.Directory.GetParent(
        Environment.GetFolderPath(Environment.SpecialFolder.System));

SxGeoSharp. Интерфейс на C# для базы данных SypexGeo. — Содержание

SxGeoSharp. Интерфейс на C# для базы данных SypexGeo. Часть I — инициализация (и введение)
     -Преамбула
     -Общая структура БД
     -Заголовок базы данных
     -Дополнительные перечисления и мелкая корректировка с придирками.
     -Поля, свойства и конструктор класса
     -Функции для чтения БД
     -Функции для чтения заголовка
     -Закрытие базы данных
     -Открытие базы данных, чтение и проверка заголовка, чтение индексов.

SxGeoSharp. Интерфейс на C# для базы данных SypexGeo. — Часть II. Поиск.
     -Трехбайтовые числа
     -Байтовый substr
     -Поиск ID или смещения в «Диапазонах IP»
     -Функция поиска в «Диапазонах» (SearchDB)

SxGeoSharp. Интерфейс на C# для базы данных SypexGeo. — Часть III. Универсальный формат упаковки данных и получение данных из справочников.
     -Приведение типов
     -Анализ (распаковка) записи
     -Обработка полей записи
     -Дополнительные функции

SxGeoSharp. Интерфейс на C# для базы данных SypexGeo. — Часть IV. Получение данных.
     -Чтение информации из справочников
     -Поиск в справочнике по ID
     -Очистка ответа
     -Функция, формирующая финальный ответ.

SxGeoSharp. Интерфейс на C# для базы данных SypexGeo. — Приложение
     -Дополнительно
     -Источники
     -Код на GitHub
     -Создатели

Скачать все в PDF
SxGeoSharp на GitHub

SxGeoSharp. Интерфейс на C# для базы данных SypexGeo. — Приложение

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

Код для отладки (тест работы со справочниками, распаковки «универсального формата» и загрузка справочников в DataSet)

Источники

Спецификация формата SxGeo Копия

Код на GitHub

https://github.com/tolik-punkoff/sxgeosharp

Создатели

БД Cypex Geo (с) 2012-2018 БИННОВАТОР
SxGeoSharp:
Идея: Leha Silent (Tolik-punkoff/Chaossoft/ОИМ)
Реализация: Werwolf aka PunkArr[] aka IogSohoth (Tolik-punkoff/Wildsoft/ОИМ)
Спонсор: OpPosition (ОИМ)

Слава Украине!

SxGeoSharp. Интерфейс на C# для базы данных SypexGeo. -Часть IV. Получение данных.

Получение кода страны по ID (для БД SxGeoCountry)

Закончим с SxGeoCountry, в которой нет справочников и информации кроме ID страны.
В оригинальном исходнике есть массив с кодами стран, а позиция в массиве, как раз ID страны из базы. Соответственно, надо просто сходить в нужный элемент массива за ISO-кодом страны.
Сделаем соответствующую функцию private string IdToIso(uint ID)

Чтение информации из справочников

Тут все просто, мы должны либо взять определенное количество байт из массива (на это дело есть bSubstr), либо прочиатать данные с диска. На это у нас есть поток SxStream и все позиции — start — откуда начинать читать, seek — сдвиг в файле, и max — максимальная длина записи в байтах.Читать далее…

SxGeoSharp. Интерфейс на C# для базы данных SypexGeo. — Часть III. Универсальный формат упаковки данных и получение данных из справочников.

Данные в справочниках хранятся в «универсальном формате упаковки данных», каждая запись идет последовательно, без разделителей. Сама запись имеет переменный размер, и состоит как из бинарных, так и из строковых данных. Вот тут разработчиками был подложен второй поросеночек — прочитать записи переменной длины и загрузить их в удобный DataSet без бубна нельзя. Третий поросеночек, к сожалению, никак не отраженный в спецификации, был в том, что числовые данные в «универсальном формате» на самом деле в little-endian! Хотя в спецификации было указано, что данные хранятся в big-endian, и все работало до того момента, когда я не попытался прочитать данные из справочников.

Читать далее…

SxGeoSharp. Интерфейс на C# для базы данных SypexGeo. — Часть II. Поиск.

Итак, наша основная задача вообще-то найти регион (страну, город) соответствующего IP-адреса. Вот этим и займемся. Точнее, найдем либо ID страны, либо смещение в файле БД, откуда потом вытащим данные. Этот функционал реализован в функции
private uint SearchID(string IP).
Читать далее

SxGeoSharp. Интерфейс на C# для базы данных SypexGeo. Часть I — инициализация (и введение)

Преамбула

Проект SypexGeo это автономная файловая бинарная база данных, хранящая IPv4 адреса, и позволяющая определить их географическую привязку, а также, соответствующий PHP-скрипт, позволяющий использовать ее на своем сайте, для определения страны и города по IP.
Примеры использования в блоге уже неоднократно описывались.
Мне понадобилось ее использовать на машине без интернета, для того, чтобы анализировать некоторые логи, на PHP (консольном) получалось довольно неуклюже и неудобно, поставить web-сервер не было возможности, потому подумал и решил, почему бы не расковырять базу, благо формат открыт, и не прикрутить к своим программам интерфейс на C#.
Сразу говорю, текст будет довольно длинный и нудный, поскольку это еще и что-то типа технической документации в вольной форме, написанной по работе, вдруг кто-то косяки поправит или кому-то понадобится.
Написан этот интерфейс наверняка не оптимально, и наверняка требует доработки. Спецификация формата написана довольно скупо, так что о кое-каких вещах приходилось догадываться, где-то, правда в мелочах, было наврано, где-то приходилось подглядывать в исходник от создателей (на PHP), где-то даже расчехлить hiew, а где-то переделывать PHP алгоритмы под C#. Это собой отдельную трудность представляло, потому что в PHP вся работа с типами благополучно переложена на интерпретатор, и PHP легко может работать с числом или с массивом байт, как со строкой, и наоборот.

Спецификация формата доступна на официальном сайте

SxGeoSharp работает с бесплатными вариантами базы SxGeoCountry (SxGeo.dat) и SxGeoCity (SxGeoCity.dat) версии 2.2 (Unicode, Windows-1251 и теоретически Latin-1). Платных вариантов баз не было, кому надо чтобы было — пишите в комментарии, договоримся.
Read more…

C#, .NET Framework 2.0 Декодировка URL-адреса без System.Web.

Понадобилось тут перекодировать URL с русскими буквами в нормальный текст, т.е. нечто вида https://ru.wikipedia.org/wiki/URL#%D0%9A%D0%BE%D0%B4%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_URL в читабельный вид https://ru.wikipedia.org/wiki/URL#Кодирование_URL

В MSDN сходу нашлось решение для .NET Framework 4, а вот для 2.0 так сразу не нашлось, но оказалось, что все-таки решение есть.

Декодирование URL

string text = https://ru.wikipedia.org/wiki/URL#%D0%9A%D0%BE%D0%B4%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_URL
text = text.Replace("+", " ");
text = System.Uri.UnescapeDataString(text);

Кодирование URL

string text = https://ru.wikipedia.org/wiki/URL#Кодирование_URL
text = System.Uri.EscapeDataString(text);

На самом деле, тот случай, когда мопед совершенно не мой, и я просто объяву разместил.

Источники

1. https://ru.wikipedia.org/wiki/URL#Кодирование_URL

2. Сам способ нашел здесь, у няшного серфера и программера.
Html and Uri String Encoding without System.Web
Там еще есть и способ кодирования эскейп-последовательностей HTML.

Ну чтоб не потерять, еще и класс на PasteBin и Mega.NZ

C#, проверка попадания IP-адреса в зарезервированные диапазоны.

Про зарезервированные диапазоны и то, как проверить, попадает ли в них адрес IPv4, я уже писал и приводил решение данной задачи на PHP. Теперь вариант на C#.

Понадобится функция IPConverter.IPToInt32(IP), которую я описывал в прошлой заметке, аналог функции ip2long() PHP

В остальном все делается также, как и в PHP-примере.

1. Заводим двумерный массив, содержащий IP начала диапазона, конечный IP и описание диапазона:

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

2. Пишем функцию, которая перебирает массив, и проверяет, попал ли указанный IP в один из диапазонов. Если попал — возвращает описание диапазона, если нет — пустую строку. IP-адреса конвертируются функцией IPConverter.IPToInt32(IP) в целые числа и сравниваются:

string CheckSpecDiaps(string IP)
{
    int uip = IPConverter.IPToInt32(IP);

    for (int i = 0; i < SpecList.GetLength(0); i++)
    {
        int start = IPConverter.IPToInt32(SpecList[i, 0]);
        int end = IPConverter.IPToInt32(SpecList[i, 1]);
        string desr = SpecList[i, 2];

        if (uip >= start && uip <= end)
        {
            return desr;
        }
    }

    return string.Empty;
}

Пример на GitHub

C#, проверка корректности и преобразования IP-адреса, класс IPAddress

Для работы с IP-адресами в .NET Framework есть класс IPAddress из пространства имен System.Net

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

bool IsIP(string IP)
{
    try
    {
        IPAddress addr = IPAddress.Parse(IP);
    }
    catch
    {
        return false;
    }
    return true;
}

Преобразование IP-адреса в полную форму записи.

Класс IPAddress и его функция Parse распознает сокращенную форму записи IP-адреса, например, адрес 4.0.0.1 можно записать как 4.1, и он будет таки корректным. Функция ToString() класса IPAddress всегда возвращает строку IP-адреса в полном виде, т.е. n.n.n.n, это можно использовать для преобразования адреса в полную форму записи:

string ToStandForm(string IP)
{
    if (!IsIP(IP))
    {
        return string.Empty;
    }
    IPAddress addr = IPAddress.Parse(IP);
    return addr.ToString();
}

Получение IP-адреса в виде массива байт

byte[] GetBytesBE(string IP)
{
    IPAddress addr = IPAddress.Parse(IP);
    return addr.GetAddressBytes();
}

Примечание: GetAddressBytes() выдает байты IP-адреса всегда в сетевом порядке, т.е. big-endian, в таком же, в котором байты идут при обычной записи IP. Если вдруг понадобится little-endian порядок, например, для того, чтобы преобразовать IP-адрес в целое число на little-endian системе, то массив надо перевернуть.

byte[] GetBytesLE(string IP)
{
    IPAddress addr = IPAddress.Parse(IP);
    byte[] addrbytes = addr.GetAddressBytes();
    Array.Reverse(addrbytes);
    return addrbytes;
}

Преобразование IP-адреса в знаковое 32-битное целое

Аналог PHP-функции ip2long() или сишной inet_addr()

int IPToInt32(string IP)
{
    IPAddress addr = IPAddress.Parse(IP);
    //получаем байты адреса, они всегда в big-endian
    byte[] addrbytes = addr.GetAddressBytes();
    //IP в виде Int32 big-endian
    int n = BitConverter.ToInt32(addrbytes,0);
    //если в системе little-endian порядок
    if (BitConverter.IsLittleEndian)
    {
        n = IPAddress.NetworkToHostOrder(n); //надо перевернуть
    }
    return n;
}

Изменить порядок байт можно и без использования Array.Reverse(), если число типа short или int. Для этого в классе IPAddress присутствуют функции NetworkToHostOrder(), которая меняет порядок байт в переменной с сетевого (big-endian) на используемый на локальной машине, и функция HostToNetworkOrder(), которая делает обратную операцию.

Преобразование IP-адреса в беззнаковое 32-битное целое

Скорее, чтоб два раза не вставать, делается точно так же, как и преобразование в знаковый int, но функции NetworkToHostOrder() и HostToNetworkOrder() тут уже не помогут, так что придется переворачивать массив байт адреса с помощью Array.Reverse():

public static uint IPToUInt32(string IP)
{
    IPAddress addr = IPAddress.Parse(IP);
    byte[] addrbytes = addr.GetAddressBytes();
    if (BitConverter.IsLittleEndian)
    {
        Array.Reverse(addrbytes);
    }
    return BitConverter.ToUInt32(addrbytes, 0);
}

Навеяно вопросом на stackowerflow
Объединил все вышеуказанные функции в отдельный класс.

Исходник на GitHub

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

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.

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