|
При нажатии на Сумма в примере 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).
Code\Glava5\StartTurnOff\Image\Window.bmp
Code\Glava5\StartTurnOff\Image\TurnOff.bmp
Code\Glava5\StartTurnOff\Image\Restart.bmp
Code\Glava5\StartTurnOff\Image\UserOut.bmp

