При нажатии на Сумма в примере ArbitraryMethod из Лекция 7, VS 2013 выдается ошибка: Необработанное исключение типа "System.InvalidOperationException" в System.Windows.Forms.dll Дополнительные сведения: Недопустимая операция в нескольких потоках: попытка доступа к элементу управления "lblResult" не из того потока, в котором он был создан. Затем: Необработанное исключение типа "System.InvalidOperationException" в mscorlib.dll Дополнительные сведения: Для каждой асинхронной операции метод EndInvoke может вызываться только один раз. |
Использование библиотек кода в windows-формах
Завершение работы Windows, перезагрузка, выход пользователя из системы
Использование функций WinAPI позволяет добавлять в свои приложения даже такую функциональность, как завершение работы системы и ее перезагрузка. Создайте новое Windows- приложение и назовите его StartTurnOff. Добавляем на форму пять кнопок и устанавливаем следующие значения формы и кнопок:
Form1, форма, свойство | Значение |
---|---|
BackgroundImage | Code\Glava5\StartTurnOff\Image\Window.bmp |
FormBorderStyle | None |
Size | 314; 400 |
StartPosition | CenterScreen |
Button3, свойство | Значение |
---|---|
Name | BtnTurnOff |
Image | Code\Glava5\StartTurnOff\Image\TurnOff.bmp |
Location | 140; 80 |
Size | 34; 34 |
Text |
Button4, свойство | Значение |
---|---|
Name | BtnRestart |
Image | Code\Glava5\StartTurnOff\Image\Restart.bmp |
Location | 238; 81 |
Size | 34; 34 |
Text |
Button5, свойство | Значение |
---|---|
Name | BtnUserOut |
Image | Code\Glava5\StartTurnOff\Image\UserOut.bmp |
Location | 198; 280 |
Size | 34; 34 |
Text |
using System; using System.Runtime.InteropServices; using System.Windows.Forms; using System.Text; namespace StartTurnOff { public class WinAPIClass { #region Завершение работы Windows. /// <summary> /// Завершение работы. /// </summary> /// <param name="shutDownType">Тип завершения работы.</param> /// <param name="reason">Причина</param> /// <returns></returns> [DllImport("user32.dll")] public static extern bool ExitWindowsEx(uint shutDownType, string reason); //To shut down or restart the system, the calling process must use the //AdjustTokenPrivileges function to enable the SE_SHUTDOWN_NAME privilege. // Для выключения или перезагрузки системы вызываемый процесс //должен использовать функцию AdjustTokenPrivileges для получения //определенного уровня доступа (привилегии) /// <summary> /// Тип завершения работы. /// </summary> public enum ShutdownType:int { LogOf = 0, Shutdown = 0x00000001, Reboot = 0x00000002, PowerOf = 0x00000008 } #endregion // Дополнительные переменные public const int SE_PRIVILEGE_ENABLED = 2; public const string SE_SHUTDOWN_NAME = "SeShutdownPrivilege"; public const int ANYSIZE_ARRAY = 1; public const int TOKEN_QUERY = 8; public const int TOKEN_ADJUST_PRIVILEGES = 32;//0x00000020; // Структура, используемая для передачи в метод AdjustTokenPrivileges // в качестве необходимой привилегии. [StructLayout(LayoutKind.Sequential, Pack=1)] internal struct TokPriv1Luid { // Количество привилегий в наборе. public int Count; public long Luid; // Тип привилегии. public int Attr; } /// <summary> /// Возвращение указателя на текущий процесс. /// </summary> /// <returns></returns> [DllImport("kernel32.dll", ExactSpelling=true) ] internal static extern IntPtr GetCurrentProcess(); /// <summary> /// Открывание процесса access token, который содержит информацию о ///безопасности текущего пользователя, /// привязанного к заданному процессу. /// </summary> /// <param name="h">Указатель на процесс.</param> /// <param name="acc">Тип access token.</param> /// <param name="phtok">Указатель на access token.</param> /// <returns></returns> [DllImport("advapi32.dll", ExactSpelling=true) ] internal static extern bool OpenProcessToken( IntPtr h, int acc, ref IntPtr phtok ); /// <summary> /// Возвращение значения переменной в текущей сессии ОС. /// </summary> /// <param name="host">Название системы. Если значение null, ///поиск осуществляется /// в локальной системе.</param> /// <param name="name">Название переменной.</param> /// <param name="pluid">Указатель на значение переменной.</param> /// <returns></returns> [DllImport("advapi32.dll") ] internal static extern bool LookupPrivilegeValue( string host, string name, ref long pluid ); /// <summary> /// Включение указанной привилегии. /// </summary> /// <param name="htok">Указатель на access token</param> /// <param name="disall">Необходимость выключения всех привилегий.</param> /// <param name="newst">Новое значение привилегии. Если предыдущее /// значение установлено в true, /// то это значение не учитывается.</param> /// <param name="len">Размер буфера для следующего значения.</param> /// <param name="prev">Предыдущая привилегия.</param> /// <param name="relen">Указатель на размер полученных привилегий.</param> /// <returns></returns> [DllImport("advapi32.dll", ExactSpelling=true) ] internal static extern bool AdjustTokenPrivileges( IntPtr htok, bool disall, ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen ); } }Листинг 5.8.
В коде формы добавляем обработчики кнопок:
private void btnCancel_Click(object sender, System.EventArgs e) { this.Close(); } private void btnUserOut_Click(object sender, System.EventArgs e) { WinAPIClass.ExitWindowsEx((uint)WinAPIClass.ShutdownType.LogOf, "Win Api test"); } private void btnTurnOff_Click(object sender, System.EventArgs e) { RestartOrShotDown(WinAPIClass.ShutdownType.Shutdown); } private void btnRestart_Click(object sender, System.EventArgs e) { RestartOrShotDown(WinAPIClass.ShutdownType.Reboot); } void RestartOrShotDown(WinAPIClass.ShutdownType type) { WinAPIClass.TokPriv1Luid tp; IntPtr hproc = WinAPIClass.GetCurrentProcess(); IntPtr hToken = IntPtr.Zero; WinAPIClass.OpenProcessToken( hproc, WinAPIClass.TOKEN_ADJUST_PRIVILEGES | WinAPIClass.TOKEN_QUERY, ref hToken ); tp.Count = 1; tp.Luid = 0; tp.Attr = WinAPIClass.SE_PRIVILEGE_ENABLED; WinAPIClass.LookupPrivilegeValue(null, WinAPIClass.SE_SHUTDOWN_NAME, ref tp.Luid ); WinAPIClass.AdjustTokenPrivileges(hToken, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero ); WinAPIClass.ExitWindowsEx((uint)type, "Win Api test"); } private void btnCancel2_Click(object sender, System.EventArgs e) { this.Close(); }Листинг 5.9.
При запуске приложения появляется совмещенное окно завершения работы и смены пользователя (рис. 5.6). Кнопки "Выключение", "Перезагрузка" и "Выход" лучше всего проверять, завершив работу со средой Visual Studio .NET.
На диске, прилагаемом к книге, вы найдете приложение StartTurnOff (Code\Glava5\StartTurnOff).
Как использовать другие функции WinAPI?
Рассмотренные примеры позволяют сделать вывод: использование функций WinAPI относится к одному из самых быстрых способов разработки приложений. При этом основная трудность заключается не в добавлении функции к приложению, а в ее поиске. В самом деле, операционная система Windows представляет чрезвычайно большой набор функций. Библиотека MSDN содержит подробное описание практически всех функций — для просмотра их списка в алфавитном порядке выбираем Пуск\Все программы\Microsoft Visual Studio .NET 2003\Microsoft Visual Studio .NET 2003 Documentation, переходим на вкладку Поиск (Search) и в строке поиска набираем WinAPI functions. В списке статей появится заголовок Functions in Alphabetical Order (Функции в алфавитном порядке) — это и есть то, что нам нужно (рис. 5.7).
Я привел методику поиска, а не расположение в содержании, — в зависимости от версии структура библиотеки может меняться, но она неизменно будет содержать список функций.
Класс String Builder
При изучении синтаксиса языка C# вы проходили класс String (пространство имен System.String) и, возможно, класс StringBuilder (пространство имен System.Text). Если эти понятия не совсем вам ясны, освежите материал — класс String описывается в любом учебнике по C#. Здесь же мы кратко рассмотрим класс StringBuilder — в следующем примере этот класс в качестве примера при вызове очередной функции WinAPI.
Одним из основных преимуществ использования этого класса является скорость работы. Упрощенно говоря, при изменении строки, созданной как экземпляр класса String, у нас создается каждый раз новый экземпляр класса, а старый уничтожается; при использовании же класса StringBuilder мы всегда работаем с одним экземпляром.
Экземпляр String Builder создается так же, как и другие экземпляры классов:
StringBuilder sb1=new StringBuilder("Hello");
Класс StringBuilder содержит только динамические методы. Рассмотрим на практике некоторые свойства и методы этого класса. Создайте новое консольное приложение и назовите его SBuilder
У класса StringBuilder нет статических методов. Все его методы — динамические. Ниже перечислены основные свойства и методы класса StringBuilder.
Свойство Length. Возвращает длину строки. Пример (здесь и далее см. рис. 5.8):
StringBuilder sb1=new StringBuilder("Hello"); int k=sb1.Length; Console.WriteLine("Длина строки sb1 "+k);
Свойство только для чтения MaxCapacity. Возвращает максимальное количество символов, которые можно записать в экземпляр класса StringBuilder. Пример:
StringBuilder sb1=new StringBuilder("Hello"); System.Console.WriteLine("Максимальное количество символов "+sb1.MaxCapacity);
Метод Append. Добавляет значение новой строки к существующему значению. Пример:
StringBuilder sb1=new StringBuilder("Hello"); StringBuilder sb2=new StringBuilder(" World "); sb1.Append(sb2); sb1.Append("!!!"); System.Console.WriteLine(sb1);
Метод Equals. Служит для сравнения двух строк. Возвращает true или false. Пример использования:
StringBuilder sb1=new StringBuilder("Hello"); StringBuilder sb2=new StringBuilder(" World "); if(sb1.Equals(sb2)) System.Console.WriteLine("Строки равны"); else System.Console.WriteLine("Строки не равны");
Метод Insert. Вставляет символы в заданную позицию (нумерация начинается с нуля). Пример использования:
StringBuilder sb3=new StringBuilder("Пртствие!"); sb3.Insert(2, "иве"); System.Console.WriteLine(sb3);
Метод Remove. Удаляет символы из строки. Первый параметр метода Remove — номер позиции, с которой начинается удаление, второй — количество удаляемых символов. Пример:
StringBuilder sb4=new StringBuilder("Казнить нельзя, помиловать"); sb4.Insert(7, ","); sb4.Remove(15, 1); System.Console.WriteLine(sb4);
Метод Replace. Заменяет символы. Пример:
StringBuilder sb5=new StringBuilder("Internet"); sb5.Replace("er", "ra"); System.Console.WriteLine(sb5);
На диске, прилагаемом к книге, вы найдете приложение SBuilder (Code\Glava5\SBuilder).