Использование неуправляемого кода

Несмотря на то что библиотека. NET Compact Framework имеет множество классов для выполнения самых разных задач, во мно­гих случаях приходится прибегать к вызовам функций Windows API. А в некоторых случаях использование функций W indows API даже предпочтительнее, чем использование аналогичных методов управляемого кода, так как они позволяют оптимизировать и по­высить производительность приложения.

Тема применения функций Windows API в .NET Compact Framework практически неисчерпаема. В некоторых случаях ис­пользование этих функций оправданно, так как других вариантов для выполнения тех или иных задач просто не существует. В то же время библиотека. NET Compact Framework постоянно развивает­ся, и часть задач с успехом решается с помощью встроенных клас­сов, добавляемых в каждой новой версии. NET Compact Framework. Поэтому разработчику придется постоянно проводить ревизию сво­их программ, заменяя в случае необходимости трудный код с ис­пользованием Windows API на код с использованием безопасного управляемого кода. NET Compact Framework.

Вызов функций Windows API

Для вызовов функций Windows API используется механизм P/Invoke. Большинство часто вызываемых функций находится в библиотеке coredlLdlL

Разработчики, которые пользовались функциями API в настоль­ной версии Windows, наверняка обратят внимание на то, что эта библиотека coredU. dll содержит множество знакомых функций из библиотек kernel32.dll, gdi32.dll и user32.dll. Поэтому во многих случаях довольно легко будет перенести свои наработки из про­грамм для настольного компьютера в приложения для мобиль­ных устройств.

Определение платформы

Если нужно определить, на какой платформе запущено ваше при­ложение, то здесь вам не обойтись без вызова функции Windows API SystemParametersInfo.

Для начала нужно создать новый класс PlatformDetector, в кото­ром следует объявить функцию SystemParametersInfo и методы оп­ределения платформы. А в обработчике события Load основной формы надо вызвать метод GetPlatform, чтобы узнать платформу сразу же после загрузки приложения, как это показано в листин­ге 13.1.

Листинг 13.1

using System;

usi ng System. Col1ecti ons. Generi с; using System. Text:

using System. Runtime. InteropServices;

namespace PI atformDetectorCS {

class PlatformDetector {

[Dl1 Import("coredl1.dll")] private static extern bool SystemParametersInfo( int uiActіon, int uiParam,

StringBuilder pvParam. int fWinlni);

private static int SPIGETPLATFORMTYPE — 257;

public static Platform GetPlatformO {

Platform plat = PI atform. Unknown;

swi tch (System. Envi ronment. OSVersion. PIatform)

{

case PlatformID. Win32NT;

plat — Platform. Win32NT; break;

case PlatformlD. WinCE:

plat — CheckWinCEPlatformO; break; продолжение

Листинг 13.1 (продолжение)

}

return plat;

}

static Platform CheckWinCEPlatformO {

Platform plat = Platform. WindowsCE;

StringBuilder strbuild = new StringBuilder(200); SystemParametersInfoCSPIGETPLATFORMTYPE, 200, strbuild. 0);

string str = strbuild. ToStringO: switch (str)

{

case "PocketPC":

plat = Platform. PocketPC: break:

case "SmartPhone":

// Note that the strbuild parameter from the // PInvoke returns "SmartPhone" with an // upper case P. The correct casing is // "Smartphone" with a lower case p. plat = Platform. Smartphone: break:

}

return plat:

}

>

public enum Platform {

PocketPC, WindowsCE, Smartphone, Win32NT, Unknown }

}

using System:

usi ng System. Col1ecti ons. Generi с:

using System. ComponentModel;

using System. Data:

using System. Drawing:

using System. Text;

using System. Windows. Forms:

namespace PI atformDetector CS

{

public partial class Forml : Form {

public Forml()

{

Ini ti alі zeComponent():

}

private void Forml_Load(object sender. EventArgs e)

{

try

{

MessageBox. Show("Платформа: " +

PIatformDetector. GetPlatform());

}

catch (Exception ex)

{

MessageBox. Show(ex. Message. ToStri ng());

}

}

}

}

Особое внимание следует обратить на комментарий. Параметр strbuild после вызова функции возвращает значение SmartPhone с большой буквой «Р», хотя более правильным вариантом считает­ся слово с маленькой буквой «р>.

Пароли

Как вы, вероятно, знаете, пользователь может установить пароль на свой карманный компьютер. Для этого ему нужно зайти в раз­дел Password при помощи последовательности команд Start ► Settings ► Password и указать четырехсимвольный пароль. С помо­щью четырех функций API можно получить сведения о пароле и даже попытаться угадать его!

Для тестирования этой возможности на форме надо разместить че­тыре кнопки и текстовое поле. Соответствующий код приведен в листинге 13.2.

Листинг 13.2

// Функция для установления нового системного пароля [Dl1Import("coredl1.dll")]

private static extern bool SetPassword(string IpszOldpassword. strі ng 1spzNewPassword);

// Функция для активации или блокировки текущего пароля [Dl1 Import("coredl1.dl1")]

private static extern bool SetPasswordActive(bool bActive, string IpszPassword);

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

[Dl1 Import("coredl1.dll")]

private static extern bool GetPasswordActiveO;

// Функция для проверки пароля [Dll Import("coredl1.dll")]

private static extern bool CheckPassword(string IpszPassword);

private void butCheckPass Click(object sender, EventArgs e)

{

txtlnfo. Text =”Активность пароля: " +

GetPasswordActiveO. ToStringO;

}

private void butNewPassClick(object sender. EventArgs e)

{

MessageBox. Show("Установка нового пароля " +

SetPassword("Активность пароля: False". txtlnfo. Text) .ToStringO);

}

private void butSetStateCl ick (object sender, EventArgs e)

{

MessageBox. Show("Отключение пароля: " +

SetPasswordActi ve(false, txtlnfo. Text) .ToStringO);

}

private void butFindPassCl ick (object sender. EventArgs e)

{

MessageBox. Show("Угадали пароль? " +

CheckPassword(txtlnfo. Text) .ToStringO);

}

ВНИМАНИЕ————————————————————————————

Будьте осторожны с данными функциями на реальном устрой­стве. Если вы случайно установите новый пароль, не запомнив его, то вам придется применить жесткую перезагрузку с потерей всех данных!

Перезагрузка КПК

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

Если разработчику необходимо программно перезагрузить устрой­ство, то необходимо воспользоваться функцией Kernel IoControl. В листинге 13.3 приведен небольшой пример, демонстрирующий мягкую перезагрузку.

Листинг 13.3

public const uint FILEDEVICEHAL = 0x00000101; public const uint METHODBUFFERED = 0; public const uint FILE_ANY_ACCESS = 0;

public uint CTL_CODE(uint DeviceType, uint Function, uint Method, uint Access)

{

return ((DeviceType « 16) | (Access « 14) | (Function «

2)

I Method);

}

[Dll Import("Coredl1.dll")] public extern static uint KernelIoControl (

uint dwIoControlCode.

IntPtr lpInBuf, uint nlnBufSize,

IntPtr lpOutBuf,

uint nOutBufSize, продолжение &

Листинг 13.3 (продолжение) ref uint lpBytesReturned );

private void butResetCl ick (object sender. EventArgs e)

{

uint bytesReturned = 0;

uint I0CTL_HAL_REB00T = CTL CODE(FILE_DEVICE_HAL. 15.

METHOD_BUFFERED. FILE ANY ACCESS);

Kernel IoControl (IOCTL HAL REBOOT. IntPtr. Zero. 0.

IntPtr. Zero, 0, ref bytesReturned);

}

Еще раз о перезагрузке

Для устройств, работающих под управлением Windows Mobile 5.0, существует более удобный способ перезагрузки. Он очень похож на код перезагрузки настольных компьютеров с использованием функции ExitWindowsEx. При этом надо обратить внимание на раз­личия карманных компьютеров и смартфонов. Если КПК можно только перезагрузить, то смартфон можно и перезагрузить, и вы­ключить. Соответствующий код приведен в листинге 13.4.

Листинг 13.4

[Dl1 Import("aygshel1.dl1")] public static extern System. Boolean ExitWindowsEx (int uFlags, int dwReserved);

const int EWXREBOOT = 2; // перезагрузка

private void butRebootCl ick (object sender, EventArgs e)

{

ExitWindowsEx(EWX REBOOT. 0);

}

Поворот экрана

Начиная с версии операционной системы PocketPC 2003 Second Edition, карманные компьютеры научились изменять ориента­цию экрана на системном уровне. Эту возможность часто исполь­зуют при создании игр, просмотре видеоматериалов или отобра­жении текстов. Если вы планируете писать программу с учетом

поворота экрана, то будет нужно проверить, поддерживает ли це­левое устройство данную функциональность. Ведь многие пользо­ватели еще владеют КПК на базе PocketPC 2000, PocketPC 2002 и PocketPC 2003.

Для поворота экрана, а также для проверки возможности такого по­ворота используется функция API ChangeDisplaySettingsEx. Данная функция использует структуру DEVM0DE. В первую очередь, в этой структуре нас интересует поле Fi el ds, в котором хранится значение Di spl ayQueryOri entati on. Этот флаг отвечает за поддержку смены ориентации экрана и передает значение в поле lpDevMode. dmDis — pl ayOri entati on. Например, значение DMO_0 говорит о том, что пово­рот экрана не поддерживается.

В листинге 13.5 приведен код, который проверяет, поддерживается ли системой изменение ориентации, и в случае положительного ответа поворачивает экран на 90°.

Листинг 13.5

// Флаг, определяющий поддержку поворота экрана private static Int32 DisplayQueryOrientation = 0x01000000:

private static Int32 CDS TEST = 2;

// запоминаем настройки экрана ScreenOrientation initialOrientation =

SystemSettі ngs. ScreenOri entati on;

[DllImport("coredl1.dll", SetLastError = true)] private extern static Int32 ChangeDisplaySettingsEx(

String deviceName,

ref DeviceMode deviceMode.

IntPtr hwnd,

Int32 flags.

IntPtr param):

struct DeviceMode {

[MarshalAsdJnmanagedType. ByValTStr, SizeConst = 32)]

public String DeviceName;

public Int16 SpecVersion;

public Int16 DriverVersion:

public Int16 Size:

public Intl6 DriverExtra; продолжение #

Листинг 13.5 (продолжение) public Int32 Fields: public Intl6 Orientation: public Int16 PaperSize: public Intl6 PaperLength: public Int16 PaperWidth; public Int16 Scale: public Int16 Copies; public Intl6 DefaultSource; public Intl6 PrintQuality; public Int16 Color; public Int16 Duplex; public Int16 YResolution: public Int16 TTOption; public Intl6 Collate;

[MarshalAsCUnmanagedType. ByValTStr. SizeConst = 32)]

public String FormName;

public Intl6 LogPixels:

public Int32 BitsPerPel;

public Int32 PelsWidth:

public Int32 PelsHeight;

public Int32 DisplayFlags:

public Int32 DisplayFrequency;

public Int32 DisplayOrientation;

}

private void butCheckRotate_Click(object sender. EventArgs e) {

// подготавливаем структуру DeviceMode DeviceMode devMode = new DeviceModeO; devMode. Size = (Intl6)Marshal. SizeOf(devMode): devMode. Fields = DisplayQueryOrientation;

// Проверяем, поддерживает ли система поворот экрана Int32 result = ChangeDisplaySettingsEx( null.

ref devMode.

IntPtr. Zero.

CDSTEST.

IntPtr. Zero);

if (result = 0)

{

// Если вызов функции прошел успешно.

// то проверяем поддержку поворота экрана

// Если параметр DisplayOrientation имеет ненулевое // значение то поворот экрана возможен if (devMode. DisplayOrientation!= 0)

{

MessageBox. Show("Поворот экрана поддерживается"): }

}

else

{

MessageBox. Show("Поворот экрана не поддерживается"): }

private void butRot90_Click(object sender. EventArgs e)

{

SystemSettі ngs. ScreenOri entati on =

ScreenOri entati on. Angle90:

}

private void butRestore_Click(object sender, EventArgs e)

{

if (SystemSettings. ScreenOrientation!= initialOrientation)

{

try

{

SystemSettings. ScreenOrientation = initialOrientation; }

catch (Exception)

{

// Unable to change the orientation back // to the original configuration.

MessageBox. Show("This sample was unable to set the " + "orientation back to the original state.");

}

}

}

Прячем кнопку Start

Функция SHFul 1 Screen позволяет прятать и показывать кнопку Start и пиктограмму виртуальной клавиатуры SIP. Соответствующий код приведен в листинге 13.6.

Листинг 13.6

III <sumnary>

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

Ill Вы можете модифицировать панель задач, панель ввода, значок

III Пуск

III </summary>

III <param name-"hwndRequester">flecKpnnTop окна</рагаш>

III <param name="dwState">OnpeflenfleT состояние окна</рагаш>

III <returns>B успешном случае возвращается True, иначе — III False</returns»

[DllImport("aygshell. dll")]

static extern uint SHFullScreen(IntPtr hwndRequester, uint dwState);

const uint SHFSSHOWTASKBAR = 0x0001: const uint SHFSHIDETASKBAR = 0x0002: const uint SHFSSHOWSIPBUTTON = 0x0004: const uint SHFSHIDESIPBUTTON = 0x0008: const uint SHFSSHOWSTARTICON = 0x0010; const uint SHFS_HIDESTARTICON = 0x0020;

private void butHideStart_Click(object sender, EventArgs e)

{

IntPtr hwnd = this. Handle;

//прячем кнопку Start

SHFul1Screen(hwnd, SHFS_HIDESTARTICON);

//прячем SIP

//SHFullScreen(hwnd, SHFS_HIDESIPBUTTON):

}

private void butShowStart_Click(object sender. EventArgs e)

{

//показываем кнопку Start IntPtr hwnd = this. Handle:

SHFullScreen(hwnd, SHFS_SHOWSTARTICON):

//показываем SIP

//SHFul1Screen(hwnd. SHFS_SHOWSIPBUTTON);

}

В примере показано, как прятать кнопку Start. Если нужно спря­тать пиктограмму SIP, то надо убрать комментарии при втором вызове функции. На рис. 13.1 показан внешний вид экрана со спря­танной кнопкой Start.

Панель задач

Скрыг« кк пгіШШь Лу Ш &

‘Л?

ЯтМ*

. .і

Использование неуправляемого кода

Рис. 13.1. Скрытие кнопки Start

Очень часто программисты в качестве шутки создают программы, которые прячут привычные для пользователя элементы интерфей­са. В предыдущем примере было показано, как можно скрыть кноп­ку Start. Теперь нужно рассмотреть пример работы с панелью задач.

Для создания тестового приложения на форме надо разместить две кнопки. Одна из них будет скрывать панель задач, а вторая — пока­зывать ее. Соответствующий код приведен в листинге 13.7.

Листинг 13.7

III <summary>

III Скрывает одно окно и активирует другое III </summary>

private const int SW_HIDE = 0;

III <summary>

III Активирует окно III </summary>

private const int SW_SH0W = 5;

[Dll Import("coredl1.dll")]

private static extern IntPtr FindWindow(string ClassName. string WindowName);

[Dll Import("coredl1.dll")]

private static extern bool ShowWindow(IntPtr hwnd. int nCmdShow);

Листинг 13.7 (продолжение)

III Прячем панель задач, чтобы пользователь не ног III нажать кнопку Start III </summary>

public static void HideTaskbarO {

IntPtr h = FindWindowC’HHTaskBar",

ShowWindow(h, SWHIDE);

}

III <summary>

III Показывает панель задач III </summary>

public static void ShowTaskBarO {

IntPtr h = FindWindowC’HHTaskBar",

ShowWindow(h, SWSHOW);

}

private void butHideTaskbar_Click(object sender, EventArgs e)

{

HideTaskbarO;

}

private void butShowTaskbarCl ick (object sender. EventArgs e)

{

ShowTaskBarO;

}

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

Запуск других приложений

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

Листинг 13.8

public class Processlnfo {

public IntPtr hProcess; public IntPtr hThread; public Int32 Processld; public Int32 Threadld;

}

[DTIImport("CoreDll. DLL", SetLastError = true)] private extern static

int CreateProcess(String imageName,

String cmdLine,

IntPtr IpProcessAttributes,

IntPtr lpThreadAttributes,

Int32 boolInheritHandles,

Int32 dwCreationFlags,

IntPtr IpEnvironment,

IntPtr IpszCurrentDir. byte[] si.

Processlnfo pi);

private void butCalcCl ick (object sender, EventArgs e)

{

//Запускаем калькулятор Processlnfo pi = new ProcessInfoO;

CreateProcessCcalc. exe", IntPtr. Zero, IntPtr. Zero. 0,

0.

IntPtr. Zero. IntPtr. Zero, new Byte[128], pi);

}

private void butCalendarCl ick (object sender, EventArgs e)

{

//Запускаем календарь Processlnfo pi = new ProcessInfoO;

CreateProcessCcalendar. exe”, IntPtr. Zero, IntPtr. Zero,

0. 0,

IntPtr. Zero. IntPtr. Zero, new Byte[128], pi);

}

private void butlnfraCl ick (object sender, EventArgs e)

{

//Посылаем файл через инфракрасное соединение _ *

продолжение &

Листинг 13.8 (продолжение)

Processlnfo pi = new ProcessInfoO:

CreateProcessCBeam. exe", "WwindowsWAlarml. wav".

IntPtr. Zero,

IntPtr. Zero. 0, 0. IntPtr. Zero, IntPtr. Zero, new Byte[128], pi);

}

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

Названия специальных файлов

В Windows существует ряд специальных папок, в которых содер­жатся файлы определенной категории. Например, в папке Избран­ное содержатся ссылки на любимые сайты пользователя.

Проблема заключается в том, что в локализованных версиях Windows эти папки зачастую имеют разные названия. Так, в аме­риканской версии Windows упомянутая папка имеет название Favorites. И если ваша программа ссылается на файл, находящий­ся в специальной папке, то необходимо точно узнать, как назы­вается эта папка на конкретном устройстве. Код проверки при­веден в листинге 13.9.

Листинг 13.9

// Константы III <summary>

III Папка, содержащая файлы и папки, которые появляются на III экране Сегодня III </summary>

const int CSIDL_DESKTOPDIRECTORY =• 0x0010;

III <summary>

III Папка Избранное /// </summary>

const int CSIDL_FAVORITES — 0x0006;

III <summary>

III Папка Мои документы /// </summary>

const int CSIDL_PERSONAL = 0x0005;

III <summary>

III Папка Програины в папке Главное неню III (WindowsStart MenuPrograms)

III </summary>

const int CSIDL_PROGRAMS = 0x0002;

III <summary>

III Папка Recent (содержит последние из открывавшихся III документов)

III </summary>

const int CSIDL_RECENT = 0x0008;

III <summary>

III Папка Главное меню III (WindowsStart Menu)

III </summary>

const int CSIDLSTARTMENU = 0x000b;

III <summary>

III Папка Автозагрузка для программ,

III которые автоматически загружаются при запуске Windows III WindowsStartUp III </summary>

const int CSIDLSTARTUP — 0x0007;

III <summary>

III Папка, в которой хранятся шаблоны документов III </summary>

const int CSIDL_TEMPLATES = 0x0015;

III <summary>

III Функция получения имен специальных папок III </summary>

[Dll Import("Coredll. dll")]

static extern int SHGetSpecialFolderPath

(IntPtr hwndOwner, StringBuilder IpszPath, int nFolder, int fCreate);

const int MAX_PATH = 260;

private void Forml_Load(object sender. EventArgs e)

{

// Папка Избранное

StringBuilder strFavorites — new StringBuilder(MAX PATH);

Листинг 13.9 (продолжение)

SHGetSpeci alFolderPath(this. Handle, strFavori tes.

CSIDL FAVORITES, 0);

MessageBox. Show("Избранное: " + strFavorites. ToStringO):

// Папка Программы

StringBuilder strPrograms = new StringBuilder(MAX PATH): SHGetSpeci alFolderPath(thi s. Handle, strPrograms, CSIDLPROGRAMS. 0):

MessageBox. Show("Программы: " + strPrograms. ToStringO):

// Мои документы

StringBuilder strMyDocs = new StringBuilder(MAXPATH): SHGetSpeci alFolderPath(thi s. Handle. strMyDocs.

CSIDLPERSONAL, 0);

MessageBox. Show ("Мои документы: " + strMyDocs. ToStringO):

}

Использование звуковых файлов

Мир современных компьютеров трудно представить без мультиме­дийных возможностей; однако проигрывание звуковых файлов не поддерживалось в библиотеке. NET Framework 1.0. Подобный под­ход Microsoft удивил многих программистов. В этом случае прихо­дилось использовать неуправляемый код с вызовом функции PlaySound.

С выходом. NET Framework 2.0 ситуация изменилась в лучшую сто­рону. Но легкая поддержка звуковых файлов остается прерогати­вой настольных систем. В библиотеке. NET Compact Framework по- прежнему отсутствует поддержка проигрывания звуковых файлов. А ведь для разработки игры наличие звуковых эффектов является обязательным условием, иначе игра будет просто неинтересна!

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

Итак, нужно создать новый проект с именем PlaySound_CS, К проек­ту надо добавить новый класс с именем Sound. Объявление функ­ции PlaySound, необходимой для проигрывания звуков, нужно по­местить в класс Sound, как показано в листинге 13.10.

Листинг 13.10

private enum Flags {

SNDSYNC — 0x0000.

SND ASYNC = 0x0001,

SND NODEFAULT — 0x0002,

SND_MEM0RY — 0x0004.

SND LOOP = 0x0008,

SNDNOSTOP — 0x0010.

SNDNOWAIT = 0x00002000.

SND_ALIAS = 0x00010000,

SNDALIASID = 0x00110000,

SNDjlLENAME = 0x00020000,

SND_RESOURCE = 0x00040004 }

[DllImportCCoreDll. DLL", EntryPoint = ■’PlaySound",

SetLastError = true)]

private extern static int PlaySound(string szSound, IntPtr hMod,

int flags):

[Dl1 Import("CoreDll. DLL". EntryPoint = “PlaySound”.

SetLastError = true)] private extern static int PIaySoundBytes(byte[] szSound, IntPtr hMod.

int flags):

Данная функция использует для параметра f1 ags несколько пре­допределенных констант. Более подробную информацию о назна­чении флагов этой функции можно найти в документации.

После этого создаются два конструктора с разными параметрами, которые будут использоваться для разных методов воспроизведения звука, и метод Play. Теперь нужно перейти к основной форме и раз­местить на ней две кнопки. Первая кнопка, butResource, будет проиг­рывать звуковой фрагмент, который хранится в ресурсах приложе­ния. Кнопка butFi 1 е запустит метод, который проигрывает аудиофайл.

Для того чтобы пример работал, понадобятся два звуковых фай­лов. В состав Windows ХР входит несколько звуковых файлов. Для данного примера использовался файл chimes. wav. Его нужно доба­вить в проект. Чтобы включить файл chimes. wav в проект как ре­сурс, надо в свойствах файла выбрать пункт Build Action и устано­вить значение Embedded Resource.

10-2873

В качестве внешнего аудиофайла будет использоваться файл alarm3.wav, входящий в состав Windows Mobile. Этот файл находит­ся в папке Windows. При желании можно использовать свой файл, но при этом надо в коде указать путь к нему. Теперь достаточно прописать код для обработки события Cl ick созданных кнопок, как показано в листинге 13.11, — и приложение готово

Листинг 13.11

using System;

usi ng System. Col1ecti ons. Generi с; using System. Text; using System.10;

using System. Runtіme. InteropServices;

namespace PlaySound CS {

public class Sound {

private byte[] m_soundBytes; private string m_fileName;

private enum Flags {

SNDSYNC — 0x0000,

SND_ASYNC = 0x0001,

SND_NODEFAULT = 0x0002,

SNDMEMORY = 0x0004,

SND LOOP — 0x0008,

SND_N0ST0P =* 0x0010,

SND_NOWAIT = 0x00002000,

SND_ALIAS = 0x00010000,

SND ALIAS ID * 0x00110000.

SND FILENAME — 0x00020000.

SNDRESOURCE = 0x00040004 }

[DI1Import("CoreDll. DLL", EntryPoint — "PlaySound",

SetLastError — true)]

private extern static int PlaySound(string szSound, IntPtr hMod,

int flags);

[DI1 Import("CoreDl1.DLL", EntryPoint = "PIaySound”, SetLastError

— true)]

private extern static int PlaySoundBytes(byte[] szSound, IntPtr hMod,

int flags);

III <summary>

III Конструктор объекта Sound, который проигрывает звук из III указанного файла III </summary>

public Sound(string fileName)

{

mfileName = fileName;

}

III <summary>

III Конструктор объекта Sound, который проигрывает звук из

III ресурсов

III </summary>

public Sound(Stream stream)

{

// читаем данные из потока

m_soundBytes * new byte[stream. Length];

stream. Read (msoundBytes, 0, (int)stream. Length);

}

III <summary>

III Воспроизводим звук III </summary> public void PlayO {

// Если из файла, то вызываем PlaySound.

// если из ресурсов, то PIaySoundBytes. if (mfileName!= null)

PlaySound(m_fileName, IntPtr. Zero. (int)(Flags. SND_ASYNC

I

FIags. SND_FILENAME));

else

PI aySoundBytes (m soundBytes. IntPtr. Zero,

(і nt)(FIags. SND_ASYNC |

FIags. SND_MEMORY));

}

}

>

10*

Теперь нужно перейти к самой форме. Код для нее приведен в листин­ге 13.12.

Листинг 13.12

using System:

usi ng System. Col1ecti ons. Generi с;

using System. ComponentModel;

using System. Data;

using System. Drawing;

using System. Text;

using System. Windows. Forms;

using System. Reflection:

namespace PlaySound CS {

public partial class Forml : Form {

public Forml()

{

Initi alі zeComponent():

Ini tі alі zeComponent();

#if DEBUG

MinimizeBox = false:

#else

MinimizeBox — true;

#endif

}

private void butResourceCl ick (object sender. EventArgs e)

{

Sound sound — new

Sound( Assembl у. GetExecutl ngAssembl у (). GetMani festResourceStream( ” PI aySoundCS. chi mes. wa v")); sound. PIay();

}

private void butFileCl ick (object sender, EventArgs e)

{

Sound sound » new Sound("WindowsWalarm3.wav"); sound. PIay();

}

Системные звуки

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

Листинг 13.13

[DllImportC"coredl1.dll")]

extern static void MessageBeepCuint BeepType);

private void butBeep_Click(object sender. EventArgs e)

{

MessageBeep(O);

}

Системное время

Чтобы получить или установить системное время на устройстве, нужно использовать функции GetSystemTime и SetSystemTime. Следу­ет учитывать, что функция GetSystemTime возвращает время по Грин­вичу, а не местное время. Код, иллюстрирующий применение этих функций, приведен В ЛИСТИНГЄІ3.14.

Листинг 13.14

using System. Runtime. InteropServices;

[Dll Import("coredll. dll")]

private extern static void GetSystemTimeCref SYSTEMTIME IpSystemTime);

[Dl1Import("coredl1.dll")]

private extern static uint SetSystemTime(ref SYSTEMTIME IpSystemTime);

private struct SYSTEMTIME {

public ushort wYear; public ushort wMonth; public ushort wDayOfWeek; public ushort wDay; public ushort wHour; public ushort wMinute; public ushort wSecond;

Листинг 13.14 (продолжение)

public ushort wMillі seconds;

}

private void GetTimeO {

// Получим системное время SYSTEMTIME st = new SYSTEMTIMEO; GetSystemTimeCref st);

DateTime dt * DateTime. UtcNow. TolocalTimeO; // Выводим сообщение MessageBox. ShowC"Текущее время: " + st. wHour. ToStringO +

+ st. wMinute. ToStringO);

private void SetTimeO {

// Сначала получим системное время SYSTEMTIME st — new SYSTEMTIMEO;

GetSystemTimeCref st);

// А теперь прибавим один час st. wHour = (ushort)(st. wHour + 1 X 24);

SetSystemTі me(ref st);

MessageBox. ShowC"Новое время: " + st. wHour. ToStringO +

+ st. wMinute. ToStringO);

}

private void butGetTimeCl ick (object sender, EventArgs e)

{

GetTimeO;

}

private void butSetTime_Click(object sender. EventArgs e)

{

SetTimeO;

}

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

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

Листинг 13.15

III <summary>

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

III <param name="szShortcut”>CTpOKa, содержащая III путь и имя создаваемого ярлыка.

///</рагаш>

III <param name="szTarget”>CTpoKa, содержащая III путь и аргументы для ярлыка.

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

Ill </param>

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

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

[Dl1 Import("coredl1.dll". EntryPoint * "SHCreateShortcut”)] private static extern bool SHCreateShortcut(string szShortcut. string szTarget):

private void butCreateShortcut_Click(object sender. EventArgs e)

{

// Создадим ярлык к калькулятору bool success = SHCreateShortcutC’WMy DocumentsWShortcut. 1 nk".

" WWi ndowsWcal с. exe""):

}

В этом примере создается ярлык Shortcut. In к для стандартного каль­кулятора, чей исполняемый файл носит имя windowscalc. exe.

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

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

Листинг 13.16

[DI1 Import("coredl1.dll”)] static extern int SendMessage(IntPtr hwnd, int msg. int wParam. int IParam):

const int EMGETLINECOUNT = OxOOBA;

private void butGetNumberCl ick (object sender. EventArgs e)

{

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

int numberOfLines • SendMessage(textBoxl. Handle.

EM GETLINECOUNT, 0. 0): sblnfo. Text — "Число строк: " + numberOfLines. ToStrіng();

}

Реестр

Реестр является важной частью любой операционной системы се­мейства 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;

usi ng System. Col1ecti ons. Generi с; using System. Text;

using System. Runtime. InteropServices;

namespace Registry CS {

class Registry {

Использование неуправляемого кода

III Создает ключ III </summary>

III <рагаш пате="кеу№те">Имя создаваемого ключа</рагат>

III <returns>B успешном случае возвращается III ERROR_SUCCESS</returns>

public static int CreateKey(UIntPtr root, string keyName)

{

UlntPtr hkey = UlntPtr. Zero; uint disposition = 0:

try

{

return RegCreateKeyEx(root. keyName. 0, null, 0, KeyAccess. None, IntPtr. Zero, ref hkey. ref disposition);

}

finally

{

if (UlntPtr. Zero!= hkey)

{

RegCloseKey(hkey);

}

}

}

III <summary>

III Удаляет ключ III </summary>

III <param name="keyName">HMfl ключа</рагат>

III <returns>B успешном случае возвращается III ERROR_SUCCESS</returns>

public static int DeleteKey(UIntPtr root, string keyName)

{

return RegDeleteKey(root, keyName);

}

III <summary>

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

III <param name="keyName">MHfl ключа</рагат>

III <param name="valueName">MMfl параметра</рагат>

III <param name=*"stringData">3Ha4eHne параметра</рагат>

III <returns>B успешном случае возвращается III ERROR_SUCCESS</returns»

public static int CreateValueString(string keyName, string valueName,

Листинг 13.17 (продолжение) string stringData)

{

UlntPtr hkey * UlntPtr. Zero: try

{

int result * RegOpenKeyExCroot, keyName. 0, KeyAccess. None. ref hkey); if (ERROR SUCCESS!= result) return result; byte[] bytes = Encoding. Unicode.6etBytes(stringData); return RegSetValueEx(hkey, valueName, 0, KeyType. String, bytes, (uint)bytes. Length);

}

finally

{

if (UlntPtr. Zero!= hkey)

{

RegCloseKey(hkey);

}

}

}

/// <summary>

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

III <param name="keyName">Имя ключа</рагат>

III <param name“"valueName">HMfl параметра</рагат>

III <param name=s"dwordData”>3Ha4eHHe параметра</рагат>

III <returns>B успешном случае возвращается III ERROR_SUCCESS</returns>

public static int CreateValueDWORD(UIntPtr root, string keyName.

string valueName, uint dwordData)

{

UlntPtr hkey = UlntPtr. Zero: try

{

int result = RegOpenKeyEx(root. keyName. 0, KeyAccess. None, ref hkey); if (ERRORSUCCESS!- result) return result; byte[] bytes — BitConverter. GetBytes(dwordData):

return RegSetValueEx(hkey, valueName, 0, KeyType. Dword, ‘■* bytes, (uint)bytes. Length);

}

finally

if (UlntPtr. Zero!= hkey)

C’ .

RegCloseKey(hkey):

)

…. }

}

III <summary>

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

III <param name="keyName’>Имя ключа</рагат>

III <param namee"valueName,’>liMfl параметра</рагат>

III <param name="dwordData,,>3Ha4eHne параметра</рагат>

III <returns>B успешном случае возвращается III ERROR SUCCESS</returns»

public static int CreateValueBinary(UIntPtr root, string keyName.

string valueName. uint binData)

{

UlntPtr hkey = UlntPtr. Zero; try

{

int result = RegOpenKeyEx(root. keyName. 0.

KeyAccess. None. ref hkey); if (ERR0R_SUCCESS!= result) return result; byte[] data = BitConverter. GetBytes(binData); return RegSetValueEx(hkey. valueName. 0. KeyType. Binary, data, (uint)data. Length);

}

finally

{

if (UlntPtr. Zero!= hkey)

{

RegCloseKey(hkey);

}

}

>

Листинг 13.17 {продолжение)

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

III <param пате="кеу№те">Имя ключа</рагаш>

III <param name="valueName">HMfl параметра</рагат>

III <param name="stringResult">CTpOKOBbie данные</рагат>

III <returns>B успешном случае возвращается III ERROR_SUCCESS</returns>

public static int 6etStringValue(UIntPtr root, string keyName. string valueName. ref string stringResult)

{

UlntPtr hkey = UlntPtr. Zero; try

{

int result “ RegOpenKeyEx(root. keyName. 0.

KeyAccess. None. ref hkey); if (ERRORSUCCESS!= 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 (UlntPtr. Zero!» hkey)

{

RegCloseKey(hkey);

}

}

}

III <summary>

III Получает заданное значение типа DWORD III </summary>

III <param паше=,,кеуМаше">Иня ключа</рагат>

III <param пате=яуа1иеНатеи>Иия паранетра</рагат>

III <param name-"dwordResul ^Значение паранетра</рагат>

III <returns>B успешной случае возвращается III ERROR_SUCCESS</returns>

public static int GetDWORDValue(UlntPtr root, string keyName. string valueName. ref uint dwordResult)

{

UlntPtr hkey — UlntPtr. Zero; try

{

int result — RegOpenKeyEx(root. keyName, 0, KeyAccess. None, ref hkey): if (ERR0R_SUCCESS!- result) return result;

byte[] bytes = null: uint length = 0;

KeyType keyType = KeyType. None:

ref

result * RegQueryValueEx(hkey, valueName, IntPtr. Zero, keyType,

null, ref length):

bytes « new byte[Marshal. S1ze0f(typeof(uint))]; length = (uint)bytes. Length: keyType — KeyType. None;

ref

result — RegQueryValueEx(hkey. valueName, IntPtr. Zero. keyType. продолжение&

Листинг 13.17 (продолжение) bytes, ref length):

if (ERRORSUCCESS!= result) return result:

dwordResult = BitConverter. ToUInt32(bytes, 0); return ERRORSUCCESS:

}

finally

{

if (UlntPtr. Zero!= hkey)

{

RegCloseKey(hkey);

III <summary>

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

III <param пате="кеуМате”>Имя ключа</рагат>

III <param name="valueName">HMfl параметра</рагат>

III <returns>B успешном случае возвращается III ERR0R_SUCCESS</returns»

public static int DeleteValue(UlntPtr root, string KeyName, string valueName)

{ “Vj,

UlntPtr hkey = UlntPtr. Zero:

{

int result — RegOpenKeyEx(root, keyName, 0, KeyAccess. None. ref hkey): if (ERROR_SUCCESS!= result) return result;

return RegDeleteValue(hkey. valueName);

}

finally

if (UlntPtr. Zero!= hkey)

{

RegCloseKey(hkey);

}

}

}

III <summary>

Ш Типы ключей III </summary> public enum KeyType : uint {

None ■ 0,

String = 1.

Binary = 3.

Dword = 4.

}

III <summary>

III Тип доступа III </summary>

public enum KeyAccess : uint {

None — 0x0000,

QueryValue = 0x0001,

SetValue = 0x0002.

CreateSubKey =» 0x0004,

EnumerateSubKeys = 0x0008,

Notify — 0x0010,

CreateLink — 0x0020 }

III <summary>

III HKEYCLASSESROOT III </summary>

public static UlntPtr HKCR = new UlntPtr(0x80000000): III <summary>

III HKEY CURRENT USER III </summary>

public static UlntPtr HKCU — new UIntPtr(0x80000001); III <summary>

III HKEY_LOCAL_MACHINE III </summary>

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

Листинг 13.17 (продолжение)

III HKEYUSERS III </summary>

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

III <summary>

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

public const int ERR0R_SUCCESS = 0;

III <summary>

III Функция для создания заданного раздела реестра. Если раздел III уже существует, то функция открывает его.

Ill </summary>

III <param name=“hkey">[in] Дескриптор к открываемому разделу III или одна из ветвей реестра:

III HKCR, HKCU, HKLM.</param>

III <param name="lpSubKey">[in] Имя для нового раздела. Данный III раздел должен быть подразделом раздела, определенного в III параметре hKey.

Ill </param>

III <param name="Reserved">[in] Зарезервированный параметр.

Ill Установлен равным 0</param>

III <param name=,,lpClass">[in] Имя класса или типа объекта III Данный параметр игнорируется, если раздел уже существует ///</рагаш>

III <param name="dw0ptions">[in] Игнорируется; установите III равным 0 III </param>

III <param name="samDesired">[in] Игнорируется: установите III равным 0 III </param>

III <param name="lpSecurityAttributes">[in] Установите в NULL. Ill </param>

III <param name=,,phkResult,,>[out] Переменная, получаемая от III дескриптора нового или открытого раздела III Если вы больше не нуждаетесь в дескрипторе, то вызовите III функцию RegCloseKey для его закрытия. </param>

III <param name="lpdwDisposition">[out] Переменная, которая III получает значение 1 (REG CREATED_NEW_KEY),

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

III и значение 2 (REGOPENEDEXISTINGJCEY), если был открыт уже ///существующий раздел

III </param>

III <returns>ERR0R SUCCESS сообщает об успешном вызове функции. ///В случае ошибки возвращается ненулевое значение III </returns>

[DI1Import("coredl1.dll". SetLastError = true)] public static extern int RegCreateKeyEx (

UlntPtr hkey.

String IpSubKey, uint Reserved.

StringBuilder lpClass. uint dwOptions.

KeyAccess samDesired.

IntPtr IpSecurityAttributes. ref UlntPtr phkResult. ref uint lpdwDisposition );

III <summary>

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

III <param name="hkey">[in] Дескриптор к удаляемому разделу или III одна из ветвей реестра: HKCR. HKCU. HKLM.

Ill </param>

III <param name="subkeyName">[in] Имя удаляемого раздела.

Ill Нельзя использовать NULL III </param>

III <returns>ERROR_SUCCESS сообщает об успешном вызове функции. Ill В случае ошибки возвращается ненулевое значение III </returns>

[DI1Import("coredl1.dl1", SetLastError — true)] public static extern int RegDeleteKey (

UlntPtr hkey. string subkeyName );

III <summary>

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

Ill </summary>

III <param name="hkey">[in] Дескриптор к открываемому разделу III или одна из ветвей реестра HKCR. HKCU. HKLM.</param>

III <param name="lpSubKey">[in] Имя открываемого раздела

Листинг 13.17 (продолжение)

/ </рагаш>

/ <рагаш name="ul0ptions">[in] Зарезервированный параметр. /Установлен равным 0</рагат>

/ <рагаш name=”samDesired">[in] Не поддерживается. Установите / в 0.</рагаш>

/ <рагагп name="phkResult">[out] Переменная, получаемая от / дескриптора открытого раздела. Если вы больше не нуждаетесь / в дескрипторе, то вызовите функцию RegCloseKey для его / закрытия</рагат>

/ <returns>ERR0R_SUCCESS сообщает об успешном вызове функции. / В случае ошибки возвращается ненулевое значение / </returns>

DllImport("coredll. dll”. SetLastError = true)] public static extern int RegOpenKeyEx (

UlntPtr hkey,

String IpSubKey. uint ulOptions.

KeyAccess samDesired. ref UlntPtr phkResult );

<summary>

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

<param name="hkey">[in] Дескриптор к открываемому разделу или одна из ветвей реестра: HKCR, HKCU. HKLM.</param> <param name“"lpValueName">[in] Значение параметра.

</param>

<param name="lpReserved">[in] Зарезервированный параметр. Установите в NULL.</param>

<param name="1рТуре">[out] Тип данных </param>

<param name="1pDatа">[out] Буфер, получающий данные.

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

<param name="lpcbData">[in/out] Размер буфера в байтах </param>

<returns>ERR0R_SUCCESS сообщает об успешном вызове функции. В случае ошибки возвращается ненулевое значение </returns>

lImport(“coredl!.dll", SetLastError — true)]

public static extern int RegQueryValueEx (

UlntPtr hkey,

String lpValueName,

IntPtr IpReserved, ref KeyType lpType, byte[] lpData, ref uint lpcbData ):

III <summary>

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

Ill </summary>

[DllImport("coredll. dll", SetLastError — true)] public static extern int ReqSetValueEx (

UlntPtr hkey,

String lpValueName. uint Reserved.

KeyType dwType. byte[] lpData. uint cbData ):

[DI1Import("coredl1.dll", SetLastError = true)] public static extern int RegDeleteValue (

UlntPtr hkey, string valueName ):

[DllImport("coredll. dll", SetLastError = true)] public static extern int RegCloseKey (

UlntPtr hkey ):

}

}

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

С помощью класса Registry разработчик может получать или уста­навливать значения параметров в реестре. Предположим, что нуж­но узнать, подключена ли к устройству внешняя клавиатура. За данную функцию отвечает параметр HasKeyboard в разделе HKEY_CUR — RENT_USERSoftwareMicrosoftShell. Если данный параметр имеет единичное значение, то система работает с подключенной внешней клавиатурой Если значение равно нулю, то клавиатуры нет. В лис­тинге 13.18 приведен код, показывающий, как можно извлечь зна­чение интересующего параметра.

Листинг 13.18

private void butCheckKeyboard_Click(object sender, EventArgs e) {

uint check * 0;

Regi stry. GetDWORDValue(Regi stry. HKCU. "SOFTWAREWMicrosoftWShell".

"HasKeyboard", ref check);

Ibllnfo. Text * Convert. ToBoolean(check).ToString();

}

В этом примере используется функция-оболочка GetDWORDVal ue из класса Regi stry. Если же вы предпочитаете обходиться без функ­ций — оболочек, а обращаться напрямую к функциям API, то пример можно переписать так, как показано в листинге 13.19.

Листинг 13.19

private static bool IsKeyboardO {

uint dwordResult:

UlntPtr hkey — UlntPtr. Zero;

try

{

int result — Registry. Reg0penKeyEx(Registry. HKCU, “SOFTWAREWMicrosoftWShel 1 0,

Regi stry. KeyAccess. None, ref hkey): if (Registry. ERR0R_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. ERR0R_SUCCESS!= result) return false;

bytes = new byte[Marshal. Size0f(typeof(uint))]: length » (uint)bytes. Length; keyType = Registry. KeyType. None; result = Registry. RegQueryValueEx(hkey. ,,HasKeyboard"> IntPtr. Zero,

ref keyType, bytes, ref length);

1f (Registry. ERR0R_SUCCESS!= result) return false;

і * ‘

dwordResult — BitConverter. ToUInt32(bytes. 0); return (dwordResult — 1);

}

finally

{

if (UlntPtr. Zero!® hkey)

{ •

Regi stry. RegCloseKey(hkey);

}

}

}

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

Листинг 13.20

// Определяем наличие внешней клавиатуры lbl Info. Text — IsKeyboardO. ToStringO;

ВНИМАНИЕ————————————————————————————

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

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

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

Щ деле HKEY_CURRENT USERXControl Panel wner. В Листинге 13.21 приве­ден код, который получает эту информацию.

Листинг 13.21

private void butOwner_Click(object sender. EventArgs e)

{

string strOwner =

Regi stry. GetStri ngValue(Regi stry. HKCU.

"WControlPanelWOwner". "Owner". ref strOwner): lblInfo. Text = strOwner;

}

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

Узнать о наличии в системе подключаемой клавиатуры можно с по­мощью функции API или просмотрев значение соответствующего ключа в реестре. Использование реестра рассматривалось несколько раньше. В листинге 13.22 приведен код, который показывает, как ■$южно узнать о присутствии подключенной клавиатуры с помощью функции API GetKeyboardStatus.

Листинг 13.22

/// <summary>

Ш Функция возвращает статус подключаемой клавиатуры и ее III возможности.

/// </summary>

III <ге1игп5>Функция возвращает битовую маску,

III показывающую присутствие клавиатуры и ее возможности III </returns>

[DI1Import("coredl1.dll")]

public static extern uint GetKeyboardStatusO;

III r~>ummary>

— Ill Показывает присутствие клавиатуры в системе III </summary>

public const uint KBDI_KEYBOARD_PRESENT = 0x0001;

III <summary>

III Показывает доступность клавиатуры. *

III Данный бит может быть изменен функцией III EnableHardwareKeyboard III </summary>

public const uint KBDIKEYBOARDENABLED = 0x0002;

/// <summary>

III Показывает наличие на клавиатуре клавиш ENTER и ESC III </summary>

public const uint KBDIKEYBOARDENTER ESC = 0x0004;

III <summary>

III Показывает наличие клавиш с буквами и цифрами III </summary>

public const uint KBDI KEYBOARDALPHA_NUM = 0x0008;

private void Forml_Load(object sender. EventArgs e)

{

MessageBox. Show("Наличие и доступность клавиатуры; " +

IsKeyboard().ToStгіng());

}

private static bool IsKeyboardO {

uint flags = KBDIKEYBOARDENABLED | KBDIKEYBOARDPRESENT: return ((GetKeyboardStatusO & flags) == flags):

}

Виброзвонок

Как правило, практически все современные модели мобильных те­лефонов и смартфонов поддерживают функцию виброзвонка. Сле­довательно, должны существовать функции для его включения и выключения. Для использования виброзвонка на смартфоне при­меняются функции Vibrate, V ibrateStop и Vi brateGetDeviceCaps.

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

Листинг 13.23

III <summary>

III Включает виброзвонок III </summary>

III <returns>S OK сообщает об успешном вызове функции. В случае

III ошибки возвращается E_FAIL

///</returns>

[Dl1Import("aygshel1")]

Листинг 13.23 (продолжение)

private static extern int Vibrate( int cvn,

IntPtr rgvn. uint fRepeat. uint dwTimeout);

/// <summary>

III Останавливает виброзвонок III </summary>

III <returns>S_OK сообщает об остановке виброзвонка. В случае III ошибки возвращается Е FAIL III </returns>

[Dl1Import("aygshel1")]

private static extern int VibrateStopO:

III <summary>

III Получает сведения о возможности использования виброзвонка III </summary>

III <param name="caps’^Перечисление VIBRATEDEVICECAPS,

III определяющее возможности воспроизведения виброзвонка III устройством.

///</param>

[Dl1Import("aygshel1")] private static extern int

VibrateGetDeviceCaps(VibrationCapabilities caps):

III <summary>

III Используется функцией VibrateGetDeviceCaps для определения III возможности воспроизведения виброзвонка.

Ill </summary>

public enum VibrationCapabilities : int {

VDC_Amplitude,

VDC_Frequency,

}

private void mnuS topV і brateCl ick (object sender. EventArgs e)

{

StopVibrateO;

}

private void mnuVibrate_Click(object sender. EventArgs e)

{

StartVibrateO;

}

III <summary>

III Включаем виброзвонок III </summary> —

III <returns>B успешном случае возвращается TRUE, в случае III ошибки — FALSE.</returns> public static bool StartVibrateO {

int result — Vibrate(0, IntPtr. Zero. Oxffffffff.

Oxffffffff):

if (result!= 0)

{

return false:

}

return true:

}

III <summary>

III Останавливаем виброзвонок III </summary>

III <returns>B успешном случае возвращается TRUE, в случае III ошибки — FALSE.</returns> public static bool StopVibrateO {

int result = VibrateStopO:

if (result!= 0)

{

return false:

}

return true;

}

ВНИМАНИЕ————————————————————————————

Приведенный код будет работать только на смартфонах. На фо­румах можно найти сообщения, что на устройствах под управлени­ем PocketPC Phone Edition этот пример не работает, даже если ука­занное устройство поддерживает виброзвонок.

Leave a reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Confirm that you are not a bot - select a man with raised hand: