52628.fb2
Несмотря на то что библиотека .NET Compact Framework имеет множество классов для выполнения самых разных задач, во многих случаях приходится прибегать к вызовам функций Windows API. А в некоторых случаях использование функций Windows API даже предпочтительнее, чем использование аналогичных методов управляемого кода, так как они позволяют оптимизировать и повысить производительность приложения.
Тема применения функций Windows API в .NET Compact Framework практически неисчерпаема. В некоторых случаях использование этих функций оправданно, так как других вариантов для выполнения тех или иных задач просто не существует. В то же время библиотека .NET Compact Framework постоянно развивается, и часть задач с успехом решается с помощью встроенных классов, добавляемых в каждой новой версии .NET Compact Framework. Поэтому разработчику придется постоянно проводить ревизию своих программ, заменяя в случае необходимости трудный код с использованием Windows API на код с использованием безопасного управляемого кода .NET Compact Framework.
Для вызовов функций Windows API используется механизм P/Invoke. Большинство часто вызываемых функций находится в библиотеке coredll.dll
.
Разработчики, которые пользовались функциями API в настольной версии Windows, наверняка обратят внимание на то, что эта библиотека coredll.dll
содержит множество знакомых функций из библиотек kernel32.dll
, gdi32.dll
и user32.dll
. Поэтому во многих случаях довольно легко будет перенести свои наработки из программ для настольного компьютера в приложения для мобильных устройств.
Если нужно определить, на какой платформе запущено ваше приложение, то здесь вам не обойтись без вызова функции Windows API SystemParametersInfo
.
Для начала нужно создать новый класс PlatformDetector
, в котором следует объявить функцию SystemParametersInfo
и методы определения платформы. А в обработчике события Load
основной формы надо вызвать метод GetPlatform
, чтобы узнать платформу сразу же после загрузки приложения, как это показано в листинге 13.1.
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace PlatformDetector_CS {
class PlatformDetector {
[DllImport("coredll.dll")]
private static extern bool SystemParametersInfo(int uiAction, int uiParam,
StringBuilder pvParam, int fWinIni);
private static int SPI_GETPLATFORMTYPE = 257;
public static Platform GetPlatform() {
Platform plat = Platform.Unknown;
switch (System.Environment.OSVersion.Platform) {
case PlatformID.Win32NT:
plat = Platform.Win32NT;
break;
case PlatformID.WinCE:
plat = CheckWinCEPlatform();
break;
}
return plat;
}
static Platform CheckWinCEPlatform() {
Platform plat = Platform.WindowsCE;
StringBuilder strbuild = new StringBuilder(200);
SystemParametersInfо(SPI_GETPLATFORMTYPE, 200, strbuild, 0);
string str = strbuild.ToString();
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;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace PlatformDetector_CS {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
try {
MessageBox.Show("Платформа: " + PlatformDetector.GetPlatform());
} catch (Exception ex) {
MessageBox.Show(ex.Message.ToString());
}
}
}
}
Особое внимание следует обратить на комментарий. Параметр strbuild
после вызова функции возвращает значение SmartPhone
с большой буквой «P», хотя более правильным вариантом считается слово с маленькой буквой «p».
Как вы, вероятно, знаете, пользователь может установить пароль на свой карманный компьютер. Для этого ему нужно зайти в раздел Password
при помощи последовательности команд Start►Settings►Password
и указать четырехсимвольный пароль. С помощью четырех функций API можно получить сведения о пароле и даже попытаться угадать его!
Для тестирования этой возможности на форме надо разместить четыре кнопки и текстовое поле. Соответствующий код приведен в листинге 13.2.
// Функция для установления нового системного пароля
[DllImport("coredll.dll")]
private static extern bool SetPassword(string lpszOldpassword,
string lspzNewPassword);
// Функция для активации или блокировки текущего пароля
[DllImport("coredll.dll")]
private static extern bool SetPasswordActive(bool bActive,
string lpszPassword);
// Функция для определения текущего состояния пароля
[DllImport("coredll.dll")]
private static extern bool GetPasswordActive();
// Функция для проверки пароля [DllImport("coredll.dll")]
private static extern bool CheckPassword(string lpszPassword);
private void butCheckPass_Click(object sender, EventArgs e) {
txtInfo.Text ="Активность пароля: " + GetPasswordActive().ToString();
}
private void butNewPass_Click(object sender, EventArgs e) {
MessageBox.Show("Установка нового пароля " +
SetPassword("Активность пароля: False", txtInfо.Text).ToString());
}
private void butSetState_Click(object sender, EventArgs e) {
MessageBox.Show("Отключение пароля: " +
SetPasswordActive(false, txtInfo.Text).ToString());
}
private void butFindPass_Click(object sender, EventArgs e) {
MessageBox.Show("Угадали пароль? " + CheckPassword(txtInfo.Text).ToString());
}
Будьте осторожны с данными функциями на реальном устройстве. Если вы случайно установите новый пароль, не запомнив его, то вам придется применить жесткую перезагрузку с потерей всех данных!
Для карманных компьютеров может применяться как жесткая, так и мягкая перезагрузка. Жесткая перезагрузка возвращает устройство в первоначальное состояние, удаляя все установленные программы. Делать жесткую перезагрузку без особой необходимости не следует. Мягкая перезагрузка является более безопасной операцией, которую часто выполняют при появлении различных сбоев в работе программ.
Если разработчику необходимо программно перезагрузить устройство, то необходимо воспользоваться функцией KernelIoControl
. В листинге 13.3 приведен небольшой пример, демонстрирующий мягкую перезагрузку.
public const uint FILE_DEVICE_HAL = 0x00000101;
public const uint METHOD_BUFFERED = 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) | Method);
}
[DllImport("Coredll.dll")]
public extern static uint KernelIoControl(
uint dwIoControlCode, IntPtr lpInBuf, uint nInBufSize, IntPtr lpOutBuf,
uint nOutBufSize, ref uint lpBytesReturned);
private void butReset_Click(object sender, EventArgs e) {
uint bytesReturned = 0;
uint IOCTL_HAL_REBOOT =
CTL_CODE(FILE_DEVICE_HAL, 15, METHOD_BUFFERED, FILE ANY ACCESS);
KernelIoControl(IOCTL_HAL_REBOOT, IntPtr.Zero, 0, IntPtr.Zero,
0, ref bytesReturned);
}
Для устройств, работающих под управлением Windows Mobile 5.0, существует более удобный способ перезагрузки. Он очень похож на код перезагрузки настольных компьютеров с использованием функции ExitWindowsEx
. При этом надо обратить внимание на различия карманных компьютеров и смартфонов. Если КПК можно только перезагрузить, то смартфон можно и перезагрузить, и выключить. Соответствующий код приведен в листинге 13.4.
[DllImport("aygshell.dll")]
public static extern System.Boolean ExitWindowsEx(int uFlags,
int dwReserved);
const int EWX_REBOOT = 2; // перезагрузка
private void butReboot_Click(object sender, EventArgs e) {
ExitWindowsEx(EWX_REBOOT, 0);
}
Начиная с версии операционной системы PocketPC 2003 Second Edition, карманные компьютеры научились изменять ориентацию экрана на системном уровне. Эту возможность часто используют при создании игр, просмотре видеоматериалов или отображении текстов. Если вы планируете писать программу с учетом поворота экрана, то будет нужно проверить, поддерживает ли целевое устройство данную функциональность. Ведь многие пользователи еще владеют КПК на базе PocketPC 2000, PocketPC 2002 и PocketPC 2003.
Для поворота экрана, а также для проверки возможности такого поворота используется функция API ChangeDisplaySettingsEx
. Данная функция использует структуру DEVMODE
. В первую очередь, в этой структуре нас интересует поле Fields
, в котором хранится значение DisplayQueryOrientation
. Этот флаг отвечает за поддержку смены ориентации экрана и передает значение в поле lpDevMode.dmDisplayOrientation
. Например, значение DMO_0
говорит о том, что поворот экрана не поддерживается.
В листинге 13.5 приведен код, который проверяет, поддерживается ли системой изменение ориентации, и в случае положительного ответа поворачивает экран на 90°.
// Флаг, определяющий поддержку поворота экрана
private static Int32 DisplayQueryOrientation = 0x01000000;
private static Int32 CDS_TEST = 2;
// запоминаем настройки экрана
ScreenOrientation initialOrientation = SystemSettings.ScreenOrientation;
[DllImport("coredll.dll", SetLastError = true)]
private extern static Int32 ChangeDisplaySettingsEx(
String deviceName, ref DeviceMode deviceMode, IntPtr hwnd,
Int32 flags, IntPtr param);
struct DeviceMode {
[MarshalAs(UnmanagedType.ByValTStr, SizeConst - 32)]
public String DeviceName;
public Int16 SpecVersion;
public Int16 DriverVersion;
public Int16 Size;
public Int16 DriverExtra;
public Int32 Fields;
public Int16 Orientation;
public Int16 PaperSize;
public Int16 PaperLength;
public Int16 PaperWidth;
public Int16 Scale;
public Int16 Copies;
public Int16 DefaultSource;
public Int16 PrintQuality;
public Int16 Color;
public Int16 Duplex;
public Int16 Yresolution;
public Int16 TTOption;
public Int16 Collate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public String FormName;
public Int16 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 DeviceMode();
devMode.Size = (Int16)Marshal.SizeOf(devMode);
devMode.Fields = DisplayQueryOrientation;
// Проверяем, поддерживает ли система поворот экрана
Int32 result =
ChangeDisplaySettingsEx(null, ref devMode, IntPtr.Zero, CDS_TEST,
IntPtr.Zero);
if (result == 0) {
// Если вызов функции прошел успешно.
// то проверяем поддержку поворота экрана
// Если параметр DisplayOrientation имеет ненулевое
// значение то поворот экрана возможен
if (devMode.DisplayOrientation != 0) {
MessageBox.Show("Поворот экрана поддерживается");
}
} else {
MessageBox.Show("Поворот экрана не поддерживается");
}
}
private void butRot90_Click(object sender, EventArgs e) {
SystemSettings.ScreenOrientation = ScreenOrientation.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.");
}
}
}
Функция SHFullScreen
позволяет прятать и показывать кнопку Start и пиктограмму виртуальной клавиатуры SIP. Соответствующий код приведен в листинге 13.6.
/// <summary>
/// Функция используется для изменения вида экрана.
/// Вы можете модифицировать панель задач, панель ввода, значок
/// Пуск
/// </summary>
/// <param name="hwndRequester">Дескриптор окна</param>
/// <param name="dwState">Определяет состояние окна</param>
/// <returns>B успешном случае возвращается True, иначе -
/// False</returns>
[DllImport("aygshell.dll")]
static extern uint SHFullScreen(IntPtr hwndRequester, uint dwState);
const uint SHFS_SHOWTASKBAR = 0x0001;
const uint SHFS_HIDETASKBAR = 0x0002;
const uint SHFS_SHOWSIPBUTTON = 0x0004;
const uint SHFS_HIDESIPBUTTON = 0x0008;
const uint SHFS_SHOWSTARTICON = 0x0010;
const uint SHFS_HIDESTARTICON = 0x0020;
private void butHideStart_Click(object sender, EventArgs e) {
IntPtr hwnd = this.Handle;
//прячем кнопку Start
SHFullScreen(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
//SHFullScreen(hwnd, SHFS_SHOWSIPBUTTON);
}
В примере показано, как прятать кнопку Start
. Если нужно спрятать пиктограмму SIP, то надо убрать комментарии при втором вызове функции. На рис. 13.1 показан внешний вид экрана со спрятанной кнопкой Start
.
Рис. 13.1. Скрытие кнопки Start
Очень часто программисты в качестве шутки создают программы, которые прячут привычные для пользователя элементы интерфейса. В предыдущем примере было показано, как можно скрыть кнопку Start
. Теперь нужно рассмотреть пример работы с панелью задач.
Для создания тестового приложения на форме надо разместить две кнопки. Одна из них будет скрывать панель задач, а вторая — показывать ее. Соответствующий код приведен в листинге 13.7.
/// <summary>
/// Скрывает одно окно и активирует другое
/// </summary>
private const int SW_HIDE = 0;
/// <summary>
/// Активирует окно
/// </summary>
private const int SW_SHOW = 5;
[DllImport("coredll.dll")]
private static extern IntPtr FindWindow(string ClassName, string WindowName);
[DllImport("coredll.dll")]
private static extern bool ShowWindow(IntPtr hwnd, int nCmdShow);
/// <summary>
/// Прячем панель задач, чтобы пользователь не мог
/// нажать кнопку Start
/// </summary>
public static void HideTaskbar() {
IntPtr h = FindWindow("HHTaskBar", "");
ShowWindow(h, SW_HIDE);
}
/// <summary>
/// Показывает панель задач
/// </summary>
public static void ShowTaskBar() {
IntPtr h = FindWindow("HHTaskBar", "");
ShowWindow(h, SW_SHOW);
}
private void butHideTaskbar_Click(object sender, EventArgs e) {
HideTaskbar();
}
private void butShowTaskbar_Click(object sender, EventArgs e) {
ShowTaskBar();
}
На самом деле с помощью функций FindWindow
и ShowWindow
можно показывать и скрывать не только панель задач, но и окна других приложений.
Иногда требуется запустить из своей программы другое приложение. В этом случае можно призвать на помощь функцию API CreateProcess
. В листинге 13.8 приведен код примера, который может запустить калькулятор, календарь и даже послать файл через инфракрасное соединение мобильному телефону. Для запуска всех этих функций на форме надо разместить всего три кнопки.
public class ProcessInfo {
public IntPtr hProcess;
public IntPtr hThread;
public Int32 ProcessId;
public Int32 ThreadId;
}
[DllImport("CoreDll.DLL", SetLastError = true)]
private extern static int CreateProcess(
String imageName, String cmdLine, IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes, Int32 boolInheritHandles,
Int32 dwCreationFlags, IntPtr lpEnvironment, IntPtr lpszCurrentDir,
byte[] si, ProcessInfo pi);
private void butCalc_Click(object sender, EventArgs e) {
//Запускаем калькулятор
ProcessInfo pi = new ProcessInfo();
CreateProcess(
"calc.exe", "", IntPtr.Zero, IntPtr.Zero, 0, 0, IntPtr.Zero, IntPtr.Zero,
new Byte[128], pi);
}
private void butCalendar_Click(object sender, EventArgs e) {
//Запускаем календарь
ProcessInfo pi = new ProcessInfo();
CreateProcess(
"calendar.exe", "", IntPtr.Zero, IntPtr.Zero, 0, 0, IntPtr.Zero,
IntPtr.Zero, new Byte[128], pi);
}
private void butInfra_Click(object sender, EventArgs e) {
//Посылаем файл через инфракрасное соединение
ProcessInfo pi = new ProcessInfo();
CreateProcess(
"Beam.exe", "\\windows\\Alarm1.wav", IntPtr.Zero, IntPtr.Zero, 0, 0,
IntPtr.Zero, IntPtr.Zero, new Byte[128], pi);
}
Приведенный код достаточно прост. Нужно вызвать функцию CreateProcess
с именем исполняемого файла в первом параметре. В методе для отправки файла также используется второй параметр, в котором указываем имя отсылаемого файла.
В Windows существует ряд специальных папок, в которых содержатся файлы определенной категории. Например, в папке Избранное содержатся ссылки на любимые сайты пользователя.
Проблема заключается в том, что в локализованных версиях Windows эти папки зачастую имеют разные названия. Так, в американской версии Windows упомянутая папка имеет название Favorites
. И если ваша программа ссылается на файл, находящийся в специальной папке, то необходимо точно узнать, как называется эта папка на конкретном устройстве. Код проверки приведен в листинге 13.9.
// Константы
/// <summary>
/// Папка, содержащая файлы и папки, которые появляются на
/// экране Сегодня
/// </summary>
const int CSIDL_DESKTOPDIRECTORY = 0x0010;
/// <summary>
/// Папка Избранное
/// </summary>
const int CSIDL_FAVORITES = 0x0006;
/// <summary>
/// Папка \Мои документы
/// </summary>
const int CSIDL_PERSONAL = 0x0005;
/// <summary>
/// Папка Программы в папке Главное меню
/// (\Windows\Start Menu\Programs)
/// </summary>
const int CSIDL_PROGRAMS = 0x0002;
/// <summary>
/// Папка Recent (содержит последние из открывавшихся
/// документов)
/// </summary>
const int CSIDL_RECENT = 0x0008;
/// <summary>
/// Папка Главное меню
/// (\Windows\Start Menu)
/// </summary>
const int CSIDL_STARTMENU = 0x000b;
/// <summary>
/// Папка Автозагрузка для программ,
/// которые автоматически загружаются при запуске Windows
/// \Windows\StartUp
/// </summary>
const int CSIDL_STARTUP = 0x0007;
/// <summary>
/// Папка, в которой хранятся шаблоны документов
/// </summary>
const int CSIDL_TEMPLATES = 0x0015;
/// <summary>
/// Функция получения имен специальных папок
/// </summary>
[DllImport("Coredll.dll")]
static extern int SHGetSpecialFolderPath
(IntPtr hwndOwner, StringBuilder lpszPath, int nFolder, int fCreate);
const int MAX_PATH = 260;
private void Form1_Load(object sender, EventArgs e) {
// Папка Избранное
StringBuilder strFavorites = new StringBuilder(MAX_PATH);
SHGetSpecialFolderPath(this.Handle, strFavorites, CSIDL_FAVORITES, 0);
MessageBox.Show("Избранное: " + strFavorites.ToString());
// Папка Программы
StringBuilder strPrograms = new StringBuilder(MAX_PATH);
SHGetSpecialFolderPath(this.Handle, strPrograms, CSIDL_PROGRAMS, 0);
MessageBox.Show("Программы: " + strPrograms.ToString());
// Мои документы
StringBuilder strMyDocs = new StringBuilder(MAX_PATH);
SHGetSpecialFolderPath(this.Handle, strMyDocs, CSIDL_PERSONAL, 0);
MessageBox.Show("Мои документы: " + strMyDocs.ToString());
}
Мир современных компьютеров трудно представить без мультимедийных возможностей; однако проигрывание звуковых файлов не поддерживалось в библиотеке .NET Framework 1.0. Подобный подход Microsoft удивил многих программистов. В этом случае приходилось использовать неуправляемый код с вызовом функции PlaySound
.
С выходом .NET Framework 2.0 ситуация изменилась в лучшую сторону. Но легкая поддержка звуковых файлов остается прерогативой настольных систем. В библиотеке .NET Compact Framework по-прежнему отсутствует поддержка проигрывания звуковых файлов. А ведь для разработки игры наличие звуковых эффектов является обязательным условием, иначе игра будет просто неинтересна!
Поэтому нужно устранить недоработку разработчиков из Microsoft. В новом примере будут использоваться два способа воспроизведения звуков. В первом случае программа будет извлекать звуковой фрагмент из ресурсов. Во втором случае программа будет проигрывать звук из обычного WAV-файла.
Итак, нужно создать новый проект с именем PlaySound_CS
. К проекту надо добавить новый класс с именем Sound
. Объявление функции PlaySound
, необходимой для проигрывания звуков, нужно поместить в класс Sound
, как показано в листинге 13.10.
private enum Flags {
SND_SYNC = 0x0000,
SND_ASYNC = 0x0001,
SND_NODEFAULT = 0x0002,
SND_MEMORY = 0x0004,
SND_LOOP = 0x0008,
SND_NOSTOP = 0x0010,
SND_NOWAIT = 0x00002000,
SND_ALIAS = 0x00010000,
SND_ALIAS_ID = 0x00110000,
SND_FILENAME = 0x00020000,
SND_RESOURCE = 0x00040004
}
[DllImport("CoreDll.DLL", EntryPoint = "PlaySound", SetLastError = true)]
private extern static int PlaySound(string szSound, IntPtr hMod, int flags);
[DllImport("CoreDll.DLL", EntryPoint = "PlaySound", SetLastError = true)]
private extern static int PlaySoundBytes(byte[] szSound, IntPtr hMod,
int flags);
Данная функция использует для параметра flags
несколько предопределенных констант. Более подробную информацию о назначении флагов этой функции можно найти в документации.
После этого создаются два конструктора с разными параметрами, которые будут использоваться для разных методов воспроизведения звука, и метод Play
. Теперь нужно перейти к основной форме и разместить на ней две кнопки. Первая кнопка, butResource
, будет проигрывать звуковой фрагмент, который хранится в ресурсах приложения. Кнопка butFilе
запустит метод, который проигрывает аудиофайл.
Для того чтобы пример работал, понадобятся два звуковых файлов. В состав Windows XP входит несколько звуковых файлов. Для данного примера использовался файл chimes.wav
. Его нужно добавить в проект. Чтобы включить файл chimes.wav
в проект как ресурс, надо в свойствах файла выбрать пункт Build Action
и установить значение Embedded Resource
.
В качестве внешнего аудиофайла будет использоваться файл alarm3.wav
, входящий в состав Windows Mobile. Этот файл находится в папке Windows
. При желании можно использовать свой файл, но при этом надо в коде указать путь к нему. Теперь достаточно прописать код для обработки события Click
созданных кнопок, как показано в листинге 13.11, — и приложение готово.
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
namespace PlaySound_CS {
public class Sound {
private byte[] m_soundBytes;
private string m_fileName;
private enum Flags {
SND_SYNC = 0x0000,
SND_ASYNC = 0x0001,
SND_NODEFAULT = 0x0002,
SND_MEMORY = 0x0004,
SND_LOOP = 0x0008,
SND_NOSTOP = 0x0010,
SND_NOWAIT = 0x00002000,
SND_ALIAS = 0x00010000,
SND_ALIAS_ID = 0x00110000,
SND_FILENAME = 0x00020000,
SND_RESOURCE = 0x00040004
}
[DllImport("CoreDll.DLL", EntryPoint = "PlaySound", SetLastError = true)]
private extern static int PlaySound(string szSound, IntPtr hMod, int flags);
[DllImport("CoreDll.DLL", EntryPoint = "PlaySound", SetLastError = true)]
private extern static int PlaySoundBytes(byte[] szSound, IntPtr hMod,
int flags);
/// <summary>
/// Конструктор объекта Sound, который проигрывает звук из
/// указанного файла
/// </summary>
public Sound(string fileName) {
m_fileName = fileName;
}
/// <summary>
/// Конструктор объекта Sound, который проигрывает звук из
/// ресурсов
/// </summary>
public Sound(Stream stream) {
// читаем данные из потока
m_soundBytes = new byte[stream.Length];
stream.Read(m_soundBytes, 0, (int)stream.Length);
}
/// <summary>
/// Воспроизводим звук
/// </summary>
public void Play() {
// Если из файла, то вызываем PlaySound.
// если из ресурсов, то PlaySoundBytes.
if (m_fileName != null)
PlaySound(m_fileName, IntPtr.Zero,
(int)(Flags.SND_ASYNC | Flags.SND_FILENAME));
else
PlaySoundBytes(m_soundBytes, IntPtr.Zero,
(int)(Flags.SND_ASYNC | Flags.SND_MEMORY));
}
}
}
Теперь нужно перейти к самой форме. Код для нее приведен в листинге 13.12.
using System;
using System.Collections.Generic;
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 Form1 : Form {
public Form1() {
InitializeComponent();
InitializeComponent();
#if DEBUG
MinimizeBox = false;
#else
MinimizeBox = true;
#endif
}
private void butResource_Click(object sender, EventArgs e) {
Sound sound =
new Sound(Assembly.GetExecutingAssembly().GetManifestResourceStream(
"PlaySound_CS.chimes.wav"));
sound.Play();
}
private void butFile_Click(object sender, EventArgs e) {
Sound sound = new Sound("Windows\\alarm3.wav");
sound.Play();
}
}
}
Также разработчик может использовать функцию MessageBeep
, позволяющую проигрывать системные звуки. Код, использующий эту функцию, приведен в листинге 13.13.
[DllImport("coredll.dll")]
extern static void MessageBeep(uint BeepType);
private void butBeep_Click(object sender, EventArgs e) {
MessageBeep(0);
}
Чтобы получить или установить системное время на устройстве, нужно использовать функции GetSystemTime
и SetSystemTime
. Следует учитывать, что функция GetSystemTime
возвращает время по Гринвичу, а не местное время. Код, иллюстрирующий применение этих функций, приведен в листинге 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.
/// <summary>
/// Функция для создания ярлыка
/// </summary>
/// <param name="szShortcut">Строка, содержащая
/// путь и имя создаваемого ярлыка.
///</param>
/// <param name="szTarget">Строка, содержащая
/// путь и аргументы для ярлыка.
/// Размер строки ограничен 256 символами.
/// </param>
/// <returns>B успешном случае возвращается TRUE,
/// в случае ошибки возвращается FALSE
/// </returns>
[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.
[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.
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace Registry_CS {
class Registry {
/// <summary>
/// Создает ключ
/// </summary>
/// <param name="keyName">Имя создаваемого ключа</param>
/// <returns>B успешном случае возвращается
/// ERROR_SUCCESS</ returns>
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);
}
}
}
/// <summary>
/// Удаляет ключ
/// </summary>
/// <param name="keyName">Имя ключа</param>
/// <returns>B успешном случае возвращается
/// ERROR_SUCCESS</returns>
public static int DeleteKey(UIntPtr root, string keyName) {
return RegDeleteKey(root, keyName);
}
/// <summary>
/// Создает строковой параметр в заданном ключе
/// </summary>
/// <param name="keyName">Имя ключа</param>
/// <param name="valueName">Имя параметра</param>
/// <param name="stringData">Значение параметра</param>
/// <returns>В успешном случае возвращается
/// ERROR_SUCCESS</returns>
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);
}
}
}
/// <summary>
/// Создает параметр типа DWORD в заданном ключе
/// </summary>
/// <param name="keyName">Имя ключа</param>
/// <param name="valueName">Имя параметра</param>
/// <param name="dwordData">Значение параметра</param>
/// <returns>В успешном случае возвращается
/// ERROR_SUCCESS</returns>
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);
}
}
}
/// <summary>
/// Создает двоичный параметр в заданном ключе
/// </summary>
/// <param name="keyName">Имя ключа</param>
/// <param name="valueName">Имя параметра</param>
/// <param name="dwordData">Значение параметра</param>
/// <returns>B успешном случае возвращается
/// ERROR_SUCCESS</returns>
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);
}
}
}
/// <summary>
/// Получает значение строкового параметра
/// </summary>
/// <param name="keyName">Имя ключа</param>
/// <param name="valueName">Имя параметра</param>
/// <param name="stringResult">строковые данные</param>
/// <returns>В успешном случае возвращается
/// ERROR_SUCCESS</returns>
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);
}
}
}
/// <summary>
/// Получает заданное значение типа DWORD
/// </summary>
/// <param name="keyName">Имя ключа</param>
/// <param name="valueName">Имя параметра</param>
/// <param name="dwordResult">Значение параметра</param>
/// <returns>B успешной случае возвращается
/// ERROR_SUCCESS</returns>
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);
}
}
}
/// <summary>
/// Удаляет заданный параметр из раздела реестра
/// </summary>
/// <param name="keyName">Имя ключа</param>
/// <param name="valueName">Имя параметра</param>
/// <returns>В успешном случае возвращается
/// ERROR_SUCCESS</returns>
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);
}
}
}
/// <summary>
/// Типы ключей
/// </summary>
public enum KeyType : uint {
None = 0,
String = 1,
Binary = 3,
Dword = 4,
}
/// <summary>
/// Тип доступа
/// </summary>
public enum KeyAccess : uint {
None = 0x0000,
QueryValue = 0x0001,
SetValue = 0x0002,
CreateSubKey = 0x0004,
EnumerateSubKeys = 0x0008,
Notify = 0x0010,
CreateLink = 0x0020
}
/// <summary>
/// HKEY_CLASSES_ROOT
/// </summary>
public static UIntPtr HKCR = new UintPtr(0x80000000);
/// <summary>
/// HKEY_CURRENT_USER
/// </summary>
public static UIntPtr HKCU = new UIntPtr(0x80000001);
/// <summary>
/// HKEY_LOCAL_MACHINE
/// </summary>
public static UIntPtr HKLM = new UIntPtr(0x80000002);
/// <summary>
/// HKEY_USERS
/// </summary>
public static UIntPtr HKU = new UintPtr(0x80000003);
/// <summary>
/// Возвращаемое значение в случае успеха
/// </summary>
public const int ERROR_SUCCESS = 0;
/// <summary>
/// Функция для создания заданного раздела реестра. Если раздел
/// уже существует, то функция открывает его.
/// </summary>
/// <param name="hkey">[in] Дескриптор к открываемому разделу
/// или одна из ветвей реестра:
/// HKCR, HKCU, HKLM.</param>
/// <param name="lpSubKey">[in] Имя для нового раздела. Данный
/// раздел должен быть подразделом раздела, определенного в
/// параметре hKey.
/// </param>
/// <param name="Reserved">[in] Зарезервированный параметр.
/// Установлен равным 0</param>
/// <param name="lpClass">[in] Имя класса или типа объекта
/// Данный параметр игнорируется, если раздел уже существует
/// </param>
/// <param name="dwOptions">[in] Игнорируется; установите
/// равным 0
/// </param>
/// <param name="samDesired">[in] Игнорируется; установите
/// равным 0
/// </param>
/// <param name="lpSecurityAttributes">[in] Установите в NULL.
/// </param>
/// <param name="phkResult">[out] Переменная, получаемая от
/// дескриптора нового или открытого раздела
/// Если вы больше не нуждаетесь в дескрипторе, то вызовите
/// функцию RegCloseKey для его закрытия. </param>
/// <param name="lpdwDisposition">[out] Переменная, которая
/// получает значение 1 (REG_CREATED_NEW_KEY),
/// если раздел был создан
/// и значение 2 (REG_OPENED_EXISTING_KEY), если был открыт уже
/// существующий раздел
/// </param>
/// <returns>ERROR_SUCCESS сообщает об успешном вызове функции.
/// В случае ошибки возвращается ненулевое значение
/// </returns>
[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);
/// <summary>
/// Функция для удаления раздела реестра
/// </summary>
/// <param name="hkey">[in] Дескриптор к удаляемому разделу или
/// одна из ветвей реестра: HKCR, HKCU, HKLM.
/// </param>
/// <param name="subkeyName">[in] Имя удаляемого раздела.
/// Нельзя использовать NULL
/// </param>
/// <returns>ERROR_SUCCESS сообщает об успешном вызове функции
/// В случае ошибки возвращается ненулевое значение
/// </returns>
[DllImport("coredll.dll", SetLastError = true)]
public static extern int RegDeleteKey(UIntPtr hkey, string subkeyName );
/// <summary>
/// Функция для открытия заданного раздела реестра.
/// </summary>
/// <param name="hkey">[in] Дескриптор к открываемому разделу
/// или одна из ветвей реестра HKCR, HKCU, HKLM.</param>
/// <param name="lpSubKey">[in] Имя открываемого раздела
/// </param>
/// <param name="ulOptions">[in] Зарезервированный параметр.
/// Установлен равным 0</param>
/// <param name="samDesired">[in] He поддерживается. Установите
/// в 0.</param>
/// <param name="phkResult">[out] Переменная, получаемая от
/// дескриптора открытого раздела. Если вы больше не нуждаетесь
/// в дескрипторе, то вызовите функцию RegCloseKey для его
/// закрытия</param>
/// <returns>ERROR_SUCCESS сообщает об успешном вызове функции.
/// В случае ошибки возвращается ненулевое значение
/// </returns>
[DllImport("coredll.dll", SetLastError = true)]
public static extern int RegOpenKeyEx(
UIntPtr hkey, String lpSubKey, uint ulOptions, KeyAccess samDesired,
ref UIntPtr phkResult);
/// <summary>
/// Функция получает тип и данные из заданного раздела реестра
/// </summary>
/// <param name="hkey">[in] Дескриптор к открываемому разделу
/// или одна из ветвей реестра: HKCR, HKCU, HKLM.</param>
/// <param name="lpValueName">[in] Значение параметра.
/// </param>
/// <param name="lpReserved">[in] Зарезервированный параметр.
/// Установите в NULL.</param>
/// <param name="lpType">[out] Тип данных
/// </param>
/// <param name="lpData">[out] Буфер, получающий данные.
/// Данный параметр может быть NULL, если данные не требуются.
/// </param>
/// <param name="lpcbData">[in/out] Размер буфера в байтах
/// </param>
/// <returns>ERROR_SUCCESS сообщает об успешном вызове функции.
/// В случае ошибки возвращается ненулевое значение
/// </returns>
[DllImport("coredll.dll", SetLastError = true)]
public static extern int RegQueryValueEx(
UIntPtr hkey, String lpValueName, IntPtr lpReserved, ref KeyType lpType,
byte[] lpData, ref uint lpcbData);
/// <summary>
/// Функция создает параметр в разделе реестра.
/// </summary>
[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 приведен код, показывающий, как можно извлечь значение интересующего параметра.
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.
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.
// Определяем наличие внешней клавиатуры
lblInfo.Text = IsKeyboard().ToString();
Следует, отметить, что при проверке примера на эмуляторе функция обнаруживает присутствие клавиатуры. Что, в общем-то, справедливо, так как с помощью обычной клавиатуры пользователь может вводить данные в программах, запущенных на эмуляторе.
Также с помощью реестра можно узнать информацию о пользователе устройства. За эту информацию отвечает параметр Owner
в разделе HKEY_CURRENT_USER\ControlPanel\Owner
. В листинге 13.21 приведен код, который получает эту информацию.
private void butOwner_Click(object sender, EventArgs e) {
string strOwner = "";
Registry.GetStringValue(Registry.HKCU, "\\ControlPanel\\Owner", "Owner",
ref strOwner);
lblInfo.Text = strOwner;
}
Узнать о наличии в системе подключаемой клавиатуры можно с помощью функции API или просмотрев значение соответствующего ключа в реестре. Использование реестра рассматривалось несколько раньше. В листинге 13.22 приведен код, который показывает, как можно узнать о присутствии подключенной клавиатуры с помощью функции API GetKeyboardStatus
.
/// <summary>
/// Функция возвращает статус подключаемой клавиатуры и ее
/// возможности.
/// </summary>
/// <returns>Функция возвращает битовую маску,
/// показывающую присутствие клавиатуры и ее возможности
/// </returns>
[DllImport("coredll.dll")]
public static extern uint GetKeyboardStatus();
/// <summary>
/// Показывает присутствие клавиатуры в системе
/// </summary>
public const uint KBDI_KEYBOARD_PRESENT = 0x0001;
/// <summary>
/// Показывает доступность клавиатуры.
/// Данный бит может быть изменен функцией
/// EnableHardwareKeyboard
/// </summary>
public const uint KBDI_KEYBOARD_ENABLED = 0x0002;
/// <summary>
/// Показывает наличие на клавиатуре клавиш ENTER и ESC
/// </summary>
public const uint KBDI_KEYBOARD_ENTER_ESC = 0x0004;
/// <summary>
/// Показывает наличие клавиш с буквами и цифрами
/// </summary>
public const uint KBDI_KEYBOARD_ALPHA_NUM = 0x0008;
private void Form1_Load(object sender, EventArgs e) {
MessageBox.Show("Наличие и доступность клавиатуры: " +
IsKeyboard().ToString());
}
private static bool IsKeyboard() {
uint flags = KBDI_KEYBOARD_ENABLED | KBDI_KEYBOARD_PRESENT;
return ((GetKeyboardStatus() & flags) == flags);
}
Как правило, практически все современные модели мобильных телефонов и смартфонов поддерживают функцию виброзвонка. Следовательно, должны существовать функции для его включения и выключения. Для использования виброзвонка на смартфоне применяются функции Vibrate
, VibrateStop
и VibrateGetDeviceCaps
.
При помощи этих функций разработчик может использовать возможности виброзвонка в своих приложениях. Соответствующий код приведен в листинге 13.23.
/// <summary>
/// Включает виброзвонок
/// </summary>
/// <returns>S_OK сообщает об успешном вызове функции. В случае
/// ошибки возвращается E_FAIL
///</returns>
[DllImport("aygshell")]
private static extern int Vibrate(int cvn, IntPtr rgvn, uint fRepeat,
uint dwTimeout);
/// <summary>
/// Останавливает виброзвонок
/// </summary>
/// <returns>S_OK сообщает об остановке виброзвонка. В случае
/// ошибки возвращается EFAIL
/// </returns>
[DllImport("aygshell")]
private static extern int VibrateStop();
/// <summary>
/// Получает сведения о возможности использования виброзвонка
/// </summary>
/// <param name="caps">Перечисление VIBRATEDEVICECAPS,
/// определяющее возможности воспроизведения виброзвонка
/// устройством.
///</param>
[DllImport("aygshell")]
private static extern int VibrateGetDeviceCaps(VibrationCapabilities caps);
/// <summary>
/// Используется функцией VibrateGetDeviceCaps для определения
/// возможности воспроизведения виброзвонка.
/// </summary>
public enum VibrationCapabilities : int {
VDC_Amplitude,
VDC_Frequency,
}
private void mnuStopVibrate_Click(object sender, EventArgs e) {
StopVibrate();
}
private void mnuVibrate_Click(object sender, EventArgs e) {
StartVibrate();
}
/// <summary>
/// Включаем виброзвонок
/// </summary>
/// <returns>В успешном случае возвращается TRUE, в случае
/// ошибки - FALSE.</returns>
public static bool StartVibrate() {
int result = Vibrate(0, IntPtr.Zero, 0xffffffff, 0xffffffff);
if (result != 0) {
return false;
}
return true;
}
/// <summary>
/// Останавливаем виброзвонок
/// </summary>
/// <returns>В успешном случае возвращается TRUE, в случае
/// ошибки - FALSE.</returns>
public static bool StopVibrate() {
int result = VibrateStop();
if (result != 0) {
return false;
}
return true;
}
Приведенный код будет работать только на смартфонах. На форумах можно найти сообщения, что на устройствах под управлением PocketPC Phone Edition этот пример не работает, даже если указанное устройство поддерживает виброзвонок.