Программирование КПК и смартфонов на .NET Compact Framework — страница 38 из 41

Чтобы получить или установить системное время на устройстве, нужно использовать функции

GetSystemTime
и
SetSystemTime
. Следует учитывать, что функция
GetSystemTime
возвращает время по Гринвичу, а не местное время. Код, иллюстрирующий применение этих функций, приведен в листинге 13.14.

Листинг 13.14

using System.Runtime.InteropServices;


[DllImport("coredll.dll")]

private extern static void GetSystemTime(ref SYSTEMTIME lpSystemTime);


[DllImport("coredll.dll")]

private extern static uint SetSystemTime(ref SYSTEMTIME lpSystemTime);


private struct SYSTEMTIME {

 public ushort wYear;

 public ushort wMonth;

 public ushort wDayOfWeek;

 public ushort wDay;

 public ushort wHour;

 public ushort wMinute;

 public ushort wSecond;

 public ushort wMilliseconds;

}


private void GetTime() {

 // Получим системное время

 SYSTEMTIME st = new SYSTEMTIME();

 GetSystemTime(ref st);

 DateTime dt = DateTime.UtcNow.ToLocalTime();

 // Выводим сообщение

 MessageBox.Show("Текущее время: " + st.wHour.ToString() + ":" +

  st.wMinute.ToString());

}


private void SetTime() {

 // Сначала получим системное время

 SYSTEMTIME st = new SYSTEMTIME();

 GetSystemTime(ref st);

 // А теперь прибавим один час

 st.wHour = (ushort)(st.wHour + 1 % 24);

 SetSystemTime(ref st);

 MessageBox.Show("Новое время: " + st.wHour.ToString() + ":" +

  st.wMinute.ToString());

}


private void butGetTime_Click(object sender, EventArgs e) {

 GetTime();

}


private void butSetTime_Click(object sender, EventArgs e) {

 SetTime();

}

Создание ярлыка

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

SHCreateShortcut
, применение которой демонстрируется в листинге 13.15.

Листинг 13.15

/// 

/// Функция для создания ярлыка

/// 

/// Строка, содержащая

/// путь и имя создаваемого ярлыка.

///

/// Строка, содержащая

/// путь и аргументы для ярлыка.

/// Размер строки ограничен 256 символами.

/// 

/// B успешном случае возвращается TRUE,

/// в случае ошибки возвращается FALSE

/// 

[DllImport("coredll.dll", EntryPoint = "SHCreateShortcut")]

private static extern bool SHCreateShortcut(string szShortcut,

 string szTarget);


private void butCreateShortcut_Click(object sender, EventArgs e) {

 // Создадим ярлык к калькулятору

 bool success = SHCreateShortcut("\\My Documents\\Shortcut.lnk",

  "\\Windows\\calс.exe\"");

}

В этом примере создается ярлык

Shortcut.lnk
для стандартного калькулятора, чей исполняемый файл носит имя
windows\calc.exe
.

Количество строк в текстовом поле

Если у текстового поля свойство

Multiline
имеет значение
True
, то свойство
Lines
возвращает массив строк в текстовом поле. Но у данного свойства есть два недостатка. Во-первых, свойство
Lines
не поддерживается библиотекой .NET Compact Framework, а во-вторых, это свойство не учитывает перенос слов. Для подсчета количества строк в многострочном текстовом поле можно использовать сообщение
EM_GETLINECOUNT
. Соответствующий код приведен в листинге 13.16.

Листинг 13.16

[DllImport("coredll.dll")]

static extern int SendMessage(IntPtr hwnd, int msg, int wParam, int lParam);


const int EM_GETLINECOUNT = 0x00BA;

private void butGetNumber_Click(object sender, EventArgs e) {

 // Узнаем число строк в текстовом поле

 int numberOfLines = SendMessage(textBox1.Handle, EM_GETLINECOUNT, 0, 0);

 sbInfo.Text = "Число строк: " + numberOfLines.ToString();

}

Реестр

Реестр является важной частью любой операционной системы семейства Windows. Не является исключением и система Windows Mobile, в которой тоже имеется собственный реестр. Однако разработчики компании Microsoft не стали включать редактор реестра в состав Windows Mobile. Поэтому для доступа к разделам реестра приходится устанавливать программы от сторонних производителей.

Однако любой программист может написать свой редактор реестра, используя возможности .NET Compact Framework. При этом следует учитывать, что в библиотеке .NET Compact Framework 2.0 появились классы для работы с разделами реестра. Если же вы продолжаете писать программы с использованием .NET Compact Framework 1.0, то придется вызывать функции Windows API.

В листинге 13.17 приведен код, который будет работать в любой версии .NET Compact Framework.

Листинг 13.17

using System;

using System.Collections.Generic;

using System.Text;

using System.Runtime.InteropServices;


namespace Registry_CS {

 class Registry {

  /// 

  /// Создает ключ

  /// 

  /// Имя создаваемого ключа

  /// B успешном случае возвращается

  /// ERROR_SUCCESS

  public static int CreateKey(UIntPtr root, string keyName) {

   UIntPtr hkey = UintPtr.Zero;

   uint disposition = 0;

   try {

    return

     RegCreateKeyEx(root, keyName, 0, null, 0, KeyAccess.None, IntPtr.Zero,

      ref hkey, ref disposition);

   } finally {

    if (UIntPtr.Zero != hkey) {

     RegCloseKey(hkey);

    }

   }

  }


  /// 

  /// Удаляет ключ

  /// 

  /// Имя ключа

  /// B успешном случае возвращается

  /// ERROR_SUCCESS

  public static int DeleteKey(UIntPtr root, string keyName) {

   return RegDeleteKey(root, keyName);

  }


  /// 

  /// Создает строковой параметр в заданном ключе

  /// 

  /// Имя ключа

  /// Имя параметра

  /// Значение параметра

  /// В успешном случае возвращается

  /// ERROR_SUCCESS

  public static int CreateValueString(string keyName, string valueName,

   string stringData) {

   UIntPtr hkey = UintPtr.Zero;

   try {

    int result = RegOpenKeyEx(root, keyName, 0, KeyAccess.None, ref hkey);

    if (ERROR_SUCCESS != result) return result;

    byte[] bytes = Encoding.Unicode.GetBytes(stringData);

    return RegSetValueEx(hkey, valueName, 0, KeyType.String, bytes,

     (uint)bytes.Length);

   } finally {

    if (UIntPtr.Zero != hkey) {

     RegCloseKey(hkey);

    }

   }

  }


  /// 

  /// Создает параметр типа DWORD в заданном ключе

  /// 

  /// Имя ключа

  /// Имя параметра

  /// Значение параметра

  /// В успешном случае возвращается

  /// ERROR_SUCCESS

  public static int CreateValueDWORD(UIntPtr root, string keyName,

   string valueName, uint dwordData) {

   UIntPtr hkey = UintPtr.Zero;

   try {

    int result = RegOpenKeyEx(root, keyName, 0, KeyAccess.None, ref hkey);

    if (ERROR_SUCCESS != result) return result;

    byte[] bytes = BitConverter.GetBytes(dwordData);

    return RegSetValueEx(hkey, valueName, 0, KeyType.Dword, bytes,

     (uint)bytes.Length);

   } finally {

    if (UIntPtr.Zero != hkey) {

     RegCloseKey(hkey);

    }

   }

  }


  /// 

  /// Создает двоичный параметр в заданном ключе

  /// 

  /// Имя ключа

  /// Имя параметра

  /// Значение параметра

  /// B успешном случае возвращается

  /// ERROR_SUCCESS

  public static int CreateValueBinary(UIntPtr root, string keyName,

   string valueName, uint binData) {

   UIntPtr hkey = UintPtr.Zero;

   try {

    int result = RegOpenKeyEx(root, keyName, 0, KeyAccess.None, ref hkey);

    if (ERROR_SUCCESS != result) return result;

    byte[] data = BitConverter.GetBytes(binData);

    return RegSetValueEx(hkey, valueName, 0, KeyType.Binary, data,

     (uint)data.Length);

   } finally {

    if (UIntPtr.Zero != hkey) {

     RegCloseKey(hkey);

    }

   }

  }


  /// 

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

  /// 

  /// Имя ключа

  /// Имя параметра

  /// строковые данные

  /// В успешном случае возвращается

  /// ERROR_SUCCESS

  public static int GetStringValue(UIntPtr root, string keyName,

   string valueName, ref string stringResult) {

   UIntPtr hkey = UintPtr.Zero;

   try {

    int result = RegOpenKeyEx(root, keyName, 0, KeyAccess.None, ref hkey);

    if (ERROR_SUCCESS != result) return result;

    byte[] bytes = null;

    uint length = 0;

    KeyType keyType = KeyType.None;

    result = RegQueryValueEx(hkey, valueName, IntPtr.Zero, ref keyType, null,

     ref length);

    if (ERROR_SUCCESS != result) return result;

    keyType = KeyType.None;

    bytes = new byte[length];

    result = RegQueryValueEx(hkey, valueName, IntPtr.Zero, ref keyType, bytes,

     ref length);

    if (ERROR SUCCESS != result) return result;

     stringResult = Encoding.Unicode.GetString(bytes, 0, bytes.Length);

    return ERROR_SUCCESS;

   } finally {

    if (UIntPtr.Zero != hkey) {

     RegCloseKey(hkey);

    }

   }

  }


  /// 

  /// Получает заданное значение типа DWORD

  /// 

  /// Имя ключа

  /// Имя параметра

  /// Значение параметра

  /// B успешной случае возвращается

  /// ERROR_SUCCESS

  public static int GetDWORDValue(UIntPtr root, string keyName,

   string valueName, ref uint dwordResult) {

   UIntPtr hkey = UintPtr.Zero;

   try {

    int result = RegOpenKeyEx(root, keyName, 0, KeyAccess.None, ref hkey);

    if (ERROR_SUCCESS != result) return result;

    byte[] bytes = null;

    uint length = 0;

    KeyType keyType = KeyType.None;

    result = RegQueryValueEx(hkey, valueName, IntPtr.Zero, ref keyType, null,

     ref length);

    bytes = new byte[Marshal.SizeOf(typeof(uint))];

    length = (uint)bytes.Length;

    keyType = KeyType.None;

    result = RegQueryValueEx(hkey, valueName, IntPtr.Zero, ref keyType, bytes,

     ref length);

    if (ERROR_SUCCESS != result) return result;

    dwordResult = BitConverter.ToUInt32(bytes, 0);

    return ERROR_SUCCESS;

   } finally {

    if (UIntPtr.Zero != hkey) {

     RegCloseKey(hkey);

    }

   }

  }


  /// 

  /// Удаляет заданный параметр из раздела реестра

  /// 

  /// Имя ключа

  /// Имя параметра

  /// В успешном случае возвращается

  /// ERROR_SUCCESS

  public static int DeleteValue(UIntPtr root, string keyName,

   string valueName) {

   UIntPtr hkey = UIntPtr.Zero;

   try {

    int result = RegOpenKeyEx(root, keyName, 0, KeyAccess.None, ref hkey);

    if (ERROR_SUCCESS != result) return result;

    return RegDeleteValue(hkey, valueName);

   } finally {

    if (UIntPtr.Zero != hkey) {

     RegCloseKey(hkey);

    }

   }

  }


  /// 

  /// Типы ключей

  /// 

  public enum KeyType : uint {

   None = 0,

   String = 1,

   Binary = 3,

   Dword = 4,

  }


  /// 

  /// Тип доступа

  /// 

  public enum KeyAccess : uint {

   None = 0x0000,

   QueryValue = 0x0001,

   SetValue = 0x0002,

   CreateSubKey = 0x0004,

   EnumerateSubKeys = 0x0008,

   Notify = 0x0010,

   CreateLink = 0x0020

  }


  /// 

  /// HKEY_CLASSES_ROOT

  /// 

  public static UIntPtr HKCR = new UintPtr(0x80000000);

  /// 

  /// HKEY_CURRENT_USER

  /// 

  public static UIntPtr HKCU = new UIntPtr(0x80000001);

  /// 

  /// HKEY_LOCAL_MACHINE

  /// 

  public static UIntPtr HKLM = new UIntPtr(0x80000002);

  /// 

  /// HKEY_USERS

  /// 

  public static UIntPtr HKU = new UintPtr(0x80000003);


  /// 

  /// Возвращаемое значение в случае успеха

  /// 

  public const int ERROR_SUCCESS = 0;


  /// 

  /// Функция для создания заданного раздела реестра. Если раздел

  /// уже существует, то функция открывает его.

  /// 

  /// [in] Дескриптор к открываемому разделу

  /// или одна из ветвей реестра:

  /// HKCR, HKCU, HKLM.

  /// [in] Имя для нового раздела. Данный

  /// раздел должен быть подразделом раздела, определенного в

  /// параметре hKey.

  /// 

  /// [in] Зарезервированный параметр.

  /// Установлен равным 0

  /// [in] Имя класса или типа объекта

  /// Данный параметр игнорируется, если раздел уже существует

  /// 

  /// [in] Игнорируется; установите

  /// равным 0

  /// 

  /// [in] Игнорируется; установите

  /// равным 0

  /// 

  /// [in] Установите в NULL.

  /// 

  /// [out] Переменная, получаемая от

  /// дескриптора нового или открытого раздела

  /// Если вы больше не нуждаетесь в дескрипторе, то вызовите

  /// функцию RegCloseKey для его закрытия. 

  /// [out] Переменная, которая

  /// получает значение 1 (REG_CREATED_NEW_KEY),

  /// если раздел был создан

  /// и значение 2 (REG_OPENED_EXISTING_KEY), если был открыт уже

  /// существующий раздел

  /// 

  /// ERROR_SUCCESS сообщает об успешном вызове функции.

  /// В случае ошибки возвращается ненулевое значение

  /// 

  [DllImport("coredll.dll", SetLastError = true)]

  public static extern int RegCreateKeyEx(

   UIntPtr hkey, String lpSubKey, uint Reserved, StringBuilder lpClass,

   uint dwOptions, KeyAccess samDesired, IntPtr lpSecurityAttributes,

   ref UIntPtr phkResult, ref uint lpdwDisposition);


  /// 

  /// Функция для удаления раздела реестра

  /// 

  /// [in] Дескриптор к удаляемому разделу или

  /// одна из ветвей реестра: HKCR, HKCU, HKLM.

  /// 

  /// [in] Имя удаляемого раздела.

  /// Нельзя использовать NULL

  /// 

  /// ERROR_SUCCESS сообщает об успешном вызове функции

  /// В случае ошибки возвращается ненулевое значение

  /// 

  [DllImport("coredll.dll", SetLastError = true)]

  public static extern int RegDeleteKey(UIntPtr hkey, string subkeyName );


  /// 

  /// Функция для открытия заданного раздела реестра.

  /// 

  /// [in] Дескриптор к открываемому разделу

  /// или одна из ветвей реестра HKCR, HKCU, HKLM.

  /// [in] Имя открываемого раздела

  /// 

  /// [in] Зарезервированный параметр.

  /// Установлен равным 0

  /// [in] He поддерживается. Установите

  /// в 0.

  /// [out] Переменная, получаемая от

  /// дескриптора открытого раздела. Если вы больше не нуждаетесь

  /// в дескрипторе, то вызовите функцию RegCloseKey для его

  /// закрытия

  /// ERROR_SUCCESS сообщает об успешном вызове функции.

  /// В случае ошибки возвращается ненулевое значение

  /// 

  [DllImport("coredll.dll", SetLastError = true)]

  public static extern int RegOpenKeyEx(

   UIntPtr hkey, String lpSubKey, uint ulOptions, KeyAccess samDesired,

   ref UIntPtr phkResult);


  /// 

  /// Функция получает тип и данные из заданного раздела реестра

  /// 

  /// [in] Дескриптор к открываемому разделу

  /// или одна из ветвей реестра: HKCR, HKCU, HKLM.

  /// [in] Значение параметра.

  /// 

  /// [in] Зарезервированный параметр.

  /// Установите в NULL.

  /// [out] Тип данных

  /// 

  /// [out] Буфер, получающий данные.

  /// Данный параметр может быть NULL, если данные не требуются.

  /// 

  /// [in/out] Размер буфера в байтах

  /// 

  /// ERROR_SUCCESS сообщает об успешном вызове функции.

  /// В случае ошибки возвращается ненулевое значение

  /// 

  [DllImport("coredll.dll", SetLastError = true)]

  public static extern int RegQueryValueEx(

   UIntPtr hkey, String lpValueName, IntPtr lpReserved, ref KeyType lpType,

   byte[] lpData, ref uint lpcbData);


  /// 

  /// Функция создает параметр в разделе реестра.

  /// 

  [DllImport("coredll.dll", SetLastError = true)]

  public static extern int RegSetValueEx(

   UIntPtr hkey, String lpValueName, uint Reserved, KeyType dwType,

   byte[] lpData, uint cbData);


  [DllImport("coredll.dll", SetLastError = true)]

  public static extern int RegDeleteValue(UIntPtr hkey, string valueName);


  [DllImport("coredll.dll", SetLastError = true)]

  public static extern int RegCloseKey(UIntPtr hkey);

 }

}

Наличие внешней клавиатуры

С помощью класса

Registry
разработчик может получать или устанавливать значения параметров в реестре. Предположим, что нужно узнать, подключена ли к устройству внешняя клавиатура. За данную функцию отвечает параметр
HasKeyboard
в разделе
HKEY_CURRENT_USER\Software\Microsoft\Shell
. Если данный параметр имеет единичное значение, то система работает с подключенной внешней клавиатурой. Если значение равно нулю, то клавиатуры нет. В листинге 13.18 приведен код, показывающий, как можно извлечь значение интересующего параметра.

Листинг 13.18

private void butCheckKeyboard_Click(object sender, EventArgs e) {

 uint check = 0;

 Registry.GetDWORDValue(Registry.HKCU, "SOFTWARE\\Microsoft\\Shell",

  "HasKeyboard", ref check);

 lblInfo.Text = Convert.ToBoolean(check).ToString();

}

В этом примере используется функция-оболочка

GetDWORDValue
из класса
Registry
. Если же вы предпочитаете обходиться без функций-оболочек, а обращаться напрямую к функциям API, то пример можно переписать так, как показано в листинге 13.19.

Листинг 13.19

private static bool IsKeyboard() {

 uint dwordResult;

 UIntPtr hkey = UIntPtr.Zero;

 try {

  int result =

   Registry.RegOpenKeyEx(Registry.HKCU, "SOFTWARE\\Microsoft\\Shell", 0,

   Registry.KeyAccess.None, ref hkey);

  if (Registry.ERROR_SUCCESS != result) return false;

  byte[] bytes = null;

  uint length = 0;

  Registry.KeyType keyType = Registry.KeyType.None;

  result =

   Registry.RegQueryValueEx(hkey, "HasKeyboard", IntPtr.Zero, ref keyType,

   null, ref length);

  if (Registry.ERROR_SUCCESS != result) return false;

  bytes = new byte[Marshal.SizeOf(typeof(uint))];

  length = (uint)bytes.Length;

  keyType = Registry.KeyType.None;

  result =

   Registry.RegQueryValueEx(hkey, "HasKeyboard", IntPtr.Zero, ref keyType,

   bytes, ref length);

  if (Registry.ERROR_SUCCESS != result) return false;

  dwordResult = BitConverter.ToUInt32(bytes, 0);

  return (dwordResult == 1);

 } finally {

  if (UIntPtr.Zero != hkey) {

   Registry.RegCloseKey(hkey);

  }

 }

}

Теперь эту функцию можно вызвать в любом месте программы, как показано в листинге 13.20.

Листинг 13.20

// Определяем наличие внешней клавиатуры

lblInfo.Text = IsKeyboard().ToString();

ВНИМАНИЕ

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

Информация о пользователе

Также с помощью реестра можно узнать информацию о пользователе устройства. За эту информацию отвечает параметр

Owner
в разделе
HKEY_CURRENT_USER\ControlPanel\Owner
. В листинге 13.21 приведен код, который получает эту информацию.

Листинг 13.21

private void butOwner_Click(object sender, EventArgs e) {

 string strOwner = "";

 Registry.GetStringValue(Registry.HKCU, "\\ControlPanel\\Owner", "Owner",

  ref strOwner);

 lblInfo.Text = strOwner;

}

Наличие дополнительной клавиатуры