пятница, 2 ноября 2012 г.

Программно выключить/перезагрузить компьютер

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

Кстати, выполнить эту задачу на C# можно несколькими способами:
  • С помощью WinAPI.
  • WMI -  инструментарий управления Windows. Кто не знает, вам сюда (рассматривать в данной статье не буду).
  • CMD команда.
Я опущу начала о том что надо создать проект типа "Библиотека классов".

WinAPI

Для начала рассмотрим самый объемный вариант - WinAPI.
В наш класс добавим API функции:
//импортируем API функцию InitiateSystemShutdown
        [DllImport("advapi32.dll", EntryPoint = "InitiateSystemShutdownEx")]
        static extern int InitiateSystemShutdown(string lpMachineName, string lpMessage, int dwTimeout, bool bForceAppsClosed, bool bRebootAfterShutdown);
        //импортируем API функцию AdjustTokenPrivileges
        [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
        internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall,
        ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);
        //импортируем API функцию GetCurrentProcess
        [DllImport("kernel32.dll", ExactSpelling = true)]
        internal static extern IntPtr GetCurrentProcess();
        //импортируем API функцию OpenProcessToken
        [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
        internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
        //импортируем API функцию LookupPrivilegeValue
        [DllImport("advapi32.dll", SetLastError = true)]
        internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);
        //импортируем API функцию LockWorkStation
        [DllImport("user32.dll", EntryPoint = "LockWorkStation")]
        static extern bool LockWorkStation();
Теперь инициализируем константы для работы API функций в соответствии с MSDN
internal const int SE_PRIVILEGE_ENABLED = 0x00000002;
internal const int TOKEN_QUERY = 0x00000008;
internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
internal const string SE_SHUTDOWN_NAME = "SeShutdownPrivilege";
ОК, все что нужно у нас есть, хотя не хватает одной мелочи - функции повышения привилегий нашего процесса. Давайте ее напишем, но сначала объявим для нее структуру:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
        internal struct TokPriv1Luid
        {
            public int Count;
            public long Luid;
            public int Attr;
        }
Ну и сама функция:
private static void SetPriv()
        {
            TokPriv1Luid tkp; //экземпляр структуры TokPriv1Luid 
            IntPtr htok = IntPtr.Zero;
            //открываем "интерфейс" доступа для своего процесса
            if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok))
            {
                //заполняем поля структуры
                tkp.Count = 1;
                tkp.Attr = SE_PRIVILEGE_ENABLED;
                tkp.Luid = 0;
                //получаем системный идентификатор необходимой нам привилегии
                LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, ref tkp.Luid);
                //повышем привилигеию своему процессу
                AdjustTokenPrivileges(htok, false, ref tkp, 0, IntPtr.Zero, IntPtr.Zero);
            }
        }
Тут много чего не понятного, немного поясню. Этот метод нужен для того чтобы гарантировать выполнения команды включения/выключения. Без нее, может просто ничего не произойти. Для управления конкретными привилегиями в WinAPI существует специальная структура TokPriv1Luid. Заполняя ее поля так:
tkp.Count = 1;
                tkp.Attr = SE_PRIVILEGE_ENABLED;
                tkp.Luid = 0;
Мы указываем на необходимость ВКЛЮЧЕНИЯ привелегии. Какой именно, в данном случае не указывается. Но перед тем как управлять привилегиями процесса, необходимо получить особый указатель на процесс - IntPtr.
В данной строчке мы получаем указатель на наш процесс:
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok)
Привилегии в системе могут быть самые разнообразные, на нужна одна - позволяющая производить перезагрузку/выключение, ее имя - SeShutdownPrivilege (подробнее о всех привилегиях можно почитать в MSDN). Для того, что бы получить возможность работать с этой привилегией - нужно получить ее идентификатор:
LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, ref tkp.Luid);
Ну и на конец, когда мы заполнили структуру "выключателя", получили идентификатор привилегии - применям все это к уже известному идентификатору процесса:
AdjustTokenPrivileges(htok, false, ref tkp, 0, IntPtr.Zero, IntPtr.Zero);
Ну вот почти все, почти перезагрузились. Добавьте этот простой код:
//публичный метод для перезагрузки/выключения машины
        public static int halt(bool RSh, bool Force)
        {
            SetPriv(); //получаем привилегия
            //вызываем функцию InitiateSystemShutdown, передавая ей необходимые параметры
            return InitiateSystemShutdown(null, null, 0, Force, RSh);
        }
        //публичный метод для блокировки операционной системы
        public static int Lock()
        {
            if (LockWorkStation())
                return 1;
            else
                return 0;
        }
Собственно класс имеет два метода: halt() - для выключения/перезагрузки, и не большим бонусом Lock() - для блокировки ОС (но не выходу из системы!)
Для использования:
halt(true, false) //мягкая перезагрузка
halt(true, true) //жесткая перезагрузка
halt(false, false) //мягкое выключение
halt(false, true) //жесткое выключение
Lock() //блокировка ОС
Ну вот и все с первым способом разобрались :). Готовую библиотеку реализующую данный метод можно скачать на этой странице под названием AReboot. Поехали дальше...

CMD команда:

Это вариант на любителя, по объему работы он гораздо меньше, давай те я покажу как выключить машину:
System.Diagnostics.Process p = new System.Diagnostics.Process();
              p.StartInfo.FileName = "cmd.exe";
              p.StartInfo.Arguments = "/c shutdown -s -t 00";
             p.Start();
Перезагрузка:
System.Diagnostics.Process p = new System.Diagnostics.Process();
              p.StartInfo.FileName = "cmd.exe";
              p.StartInfo.Arguments = "/c reboot-s -t 00";
             p.Start();
Вот и все :) Используйте тот метод который вам больше нравиться, и отвечает вашей идеологии. Существует еще много способов выполнить поставленную задачу, те что были озвучены - основные. Пробуйте, экспериментируйте, успехов вам.

P.S. Озвученный тут метод с WMI я опишу будущей статье, когда будет время. 

среда, 31 октября 2012 г.

Плавная смена цвета элементов WinForms

Вот прошло совсем не много времени, и новая публикация. Сегодня я покажу как сделать интересный эффект смены цвета.
Демонстрацию буду проводить на следующем примере: Заменить текст в элементе label по клику по кнопке, но смена текста должна происходить плавно. Так как лучше один раз показать, чем 100 раз рассказать, то тут вы можете скачать ДЕМО.
Для начала создадим проект WinForms и добавим на форму: Label и Button.

Вот код формы:
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    private void button1_Click(object sender, EventArgs e)
    {
        dR = label1.BackColor.R - label1.ForeColor.R;
        dG = label1.BackColor.G - label1.ForeColor.G;
        dB = label1.BackColor.B - label1.ForeColor.B;
        sign = 1;
        Timer timer = new Timer();
        timer.Interval = 100;
        timer.Tick += timer_Tick;
        timer.Start();
    }
    int dR, dG, dB, sign;
    void timer_Tick(object sender, EventArgs e)
    {
        if (Math.Abs(label1.ForeColor.R - label1.BackColor.R) < Math.Abs(dR / 10))
        {
            sign *= -1;
            label1.Text = "Новый текст";
        }            
        label1.ForeColor = Color.FromArgb(255, label1.ForeColor.R + sign * dR / 10, label1.ForeColor.G + sign * dG / 10, label1.ForeColor.B + sign * dB / 10);
        if (label1.BackColor.R == label1.ForeColor.R + dR)
        {
            ((Timer)sender).Stop();
        }
    }
}
При запуске приложения, в label один текст. После нажатия на кнопку, он блекнет, потом проявляется новый текст. Вся анимация занимает 2 секунды... Основная идея в том, что изменения свойств должны идти не в цикле и за доли секунды, а через таймер и хоть какое то время, которое будет различать пользователь.

Этот же прием можно использовать для любого элемента интерфейса WinForms, не ограничивайте себя, пробуйте.

P.S. Если хотите писать красивости в пользовательском интерфейсе, переходите на XAML, там все это намного проще...

вторник, 30 октября 2012 г.

Сортировка пузырьком и быстрая сортировка на C#

Привет, читатель :) Вот я и решил начать, причем начать я решил не с азов (я считаю что рассматривать их не стоит) но и чего-то запредельно сложного тут не найти. Я всего лишь студент который учиться как и ты.
Итак, первое о чем я хотел написать, так это о сортировке и ее реализации. Почему именно с нее? все просто, очень часто на собеседованиях требуют знания как работает тот или иной метод сортировки, или просят его реализовать, а во вторых всегда приятнее уметь самому делать то что уже реализовано. Так что то я заговорился не по делу, пора начинать.

Задача: Реализовать *.DLL библиотеку которая будет реализовывать сортировку массива методом пузырька или быстрой сортировкой по возрастанию и убыванию.

Так с задачей мы определились, начнем реализовывать.
Подробно о этих методах сортировки можно ознакомиться тут:
Сортировка пузырькомБыстрая сортировка.
Так с теорией мы тоже разобрались :) Быстро продвигаемся.
Начнем с реализации метода пузырька, так как он проще: Создадим класс с именем "BubbleSort.cs"

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ASort
{
    public class BubbleSort
    {
    }
}

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

private  bool IsDown;
public bool IsDownValue
{
    get
    {
        return IsDown;
    }
    set
    {
        IsDown = value;
    }
}

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

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

public Array Sort(T[] arr)where T:IComparable
{
//тут происходит сортировка
return arr;
}


Я продемонстрирую как сделать сортировку по возрастанию, для убывания вы с легкостью сделаете сами:

for (int i = 0; i < arr.Length; i++)
            {
                for (int j = 0; j < arr.Length-1; j++)
                {
                    if (!IsDownValue)
                    {
                        if (arr[j].CompareTo(arr[j + 1]) > 0)//возрастание
                        {
                            T temp = arr[j];
                            arr[j] = arr[j + 1];
                            arr[j + 1] = temp;
                        }
                    }
                    else
                    {
                        //по аналогии с предыдущем условием делаем для убывания
                    }
                }
            }

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

Теперь давай те попробуем сделать тоже самое но методом быстрой сортировки. Создадим новый класс в проекте назовем, например "QuickSort.cs" и как и в прошлый раз создадим в нем точно такое же свойство.

C# с обобщенными типами, тип Т должен реализовывать интерфейс IComparable

int partition( T[] m, int a, int b) where T :IComparable
{
    int i = a;
    for (int j = a; j <= b; j++)         // просматриваем с a по b
    {        
        if (m[j].CompareTo( m[b]) <= 0)  // если элемент m[j] не превосходит m[b],
        {
            T t = m[i];                  // меняем местами m[j] и m[a], m[a+1], m[a+2] и так далее...
            m[i] = m[j];                 // то есть переносим элементы меньшие m[b] в начало,
            m[j] = t;                    // а затем и сам m[b] «сверху»
            i++;                         // таким образом последний обмен: m[b] и m[i], после чего i++
        }
    }
    return i - 1;                        // в индексе i хранится <новая позиция элемента m[b]> + 1
}
 
void quicksort( T[] m, int a, int b) where T : IComparable// a - начало подмножества, b - конец
{                                        // для первого вызова: a = 0, b = <элементов в массиве> - 1
    if (a >= b) return;
    int c = partition( m, a, b);
    quicksort( m, a, c - 1);
    quicksort( m, c + 1, b);
}
 
//Пример вызова
//double[] arr = {9,1.5,34.4,234,1,56.5};
//quicksort(arr,0,arr.Length-1);

Теперь добавим возможность выбора сортировки по возрастанию и убыванию.

private int partition(T[] arr, int start, int end) where T : IComparable
        {
            int i = start;
            for (int j = start; j <= end; j++)
            {
                if (!IsDownValue)
                {
                    if (arr[j].CompareTo(arr[end]) <= 0)
                    {
                        T t = arr[i];
                        arr[i] = arr[j];
                        arr[j] = t;
                        i++;
                    }
                }
                else
                {
                    if (arr[j].CompareTo(arr[end]) >= 0)
                    {
                        T t = arr[i];
                        arr[i] = arr[j];
                        arr[j] = t;
                        i++;
                    }
                }
            }
            return i - 1;
        }
Ну вот и все. Все готово. Советую собирать проект в виде dll и использовать как библиотеку, как ее использовать описано тут в заголовке ASort, там же можно скачать то что получилось у меня.

Не давно нашел пример быстрой сортировки с помощью лямбда выражений:

using System;
using System.Collections.Generic;
using System.Linq;
 
static public class Qsort
    {
        public static IEnumerable QuickSort(this IEnumerable list) where T : IComparable
        {
            if (!list.Any())
            {
                return Enumerable.Empty();
            }
            var pivot = list.First();
            var smaller = list.Skip(1).Where(item => item.CompareTo(pivot) <= 0).QuickSort();
            var larger = list.Skip(1).Where(item => item.CompareTo(pivot) > 0).QuickSort();
 
            return smaller.Concat(new[] { pivot }).Concat(larger);
        }
 
//(тоже самое, но записанное в одну строку, без объявления переменных)
 
        public static IEnumerable shortQuickSort(this IEnumerable list) where T : IComparable
        {
            return !list.Any() ? Enumerable.Empty() : list.Skip(1).Where(
                item => item.CompareTo(list.First()) <= 0).shortQuickSort().Concat(new[] { list.First() })
                    .Concat(list.Skip(1).Where(item => item.CompareTo(list.First()) > 0).shortQuickSort());
        }       
    }
Ну вот и все, спасибо за внимание.