Статьи

Windows Phone Native Series — Выход из приложения, PlaySystemSound, GetSystemInformation

В качестве продолжения серии Windows Phone Native я решил добавить несколько очень простых методов в нативную оболочку, с которой я работаю. Он действует как расширение стандартного SDK и может вызывать возможности, которые (пока) не предоставляются через управляемые API.

ПРИМЕЧАНИЕ: я предполагаю, что вы уже следите за моей серией, и у вас есть основы для расширения проекта. Если нет, зайдите сюда и прочитайте вводную статью.

выход

Допустим, вы хотите выйти из приложения. Существует стандартный метод Exit, который можно реализовать, добавив ссылку на Microsoft.Xna.Framework . Общепринятой практикой является не использовать его в приложениях Silverlight, когда есть более приемлемые методы, такие как очистка backstack. Тем не менее, в исследовательских целях я должен отметить, что существует также способ естественного выхода из приложения и даже связывания с ним кода выхода . Взгляните на этот метод:

STDMETHODIMP CNativeAPI::ExitApplication()
{
	exit(0);

	return S_OK;
}

Просто вызвав метод выхода, я могу завершить свое собственное приложение. Все, что необходимо сделать, это добавить соответствующие объявления в IDL и заголовочные файлы.

MessageBeep

Двигаясь дальше, вы теперь хотите воспроизвести системный звуковой сигнал. Существует функция MessageBeep, что в стандартной ОС Windows имеет несколько возможных вариантов, как  MB_ICONINFORMATION , MB_ICONWARNING или MB_OK . В этом случае существует только один стандартный звук (их должно быть больше, учитывая, что эти данные параметризованы в Guide.BeginShowMessageBox), который можно передать через значение uint. Так что метод довольно прост:

STDMETHODIMP CNativeAPI::PlaySystemSound(UINT sound)
{
	BOOL result = MessageBeep(sound);

	if (result)
		return S_OK;
	else
		return 0x80070000 | ::GetLastError();
}

Вы можете спросить — почему я прошу пользователя добавить значение uint, когда доступен только один звук? Сам метод MessageBeep не работает без параметра. Поэтому жесткое кодирование значения не имеет большого смысла, поскольку настройки ОС могут в конечном итоге измениться.

GetSystemInfo

Эта функция вызывается для получения информации о процессоре, на котором работает ОС. Он опирается на структуру SYSTEM_INFO, которая содержит связанные значения DWORD и WORD . Вот быстрый и грязный подход для чтения системных данных:

STDMETHODIMP CNativeAPI::GetSystemInformation(SYSTEM_DATA* retData)
{
	SYSTEM_DATA sysData;
	
	SYSTEM_INFO data;
	GetSystemInfo(&data);

	sysData.AllocationGranularity = data.dwAllocationGranularity;
	sysData.NumberOfProcessors = data.dwNumberOfProcessors;
	sysData.PageSize = data.dwPageSize;
	sysData.OEMID = data.dwOemId;
	sysData.ProcessorType = data.dwProcessorType;
	sysData.ActiveProcessorMask = data.dwActiveProcessorMask;
	sysData.ProcessorLevel = data.wProcessorLevel;
	sysData.ProcessorRevision = data.wProcessorRevision;
	sysData.ProcessorArchitecture = data.wProcessorArchitecture;
	
	*retData = sysData;

	return S_OK;
}

Вы можете видеть, что существует также так называемая структура SYSTEM_DATA. Это пользовательская реализация, которую я представил для удобства чтения и переносимости. Он объявлен в файле IDL, связанном с основной библиотекой. Именно так:

typedef struct _SYSTEM_DATA {
	DWORD PageSize;
	DWORD ActiveProcessorMask;
	DWORD NumberOfProcessors;
	DWORD ProcessorType;
	DWORD AllocationGranularity;
	WORD ProcessorLevel;
	WORD ProcessorRevision;
	DWORD OEMID;
	WORD ProcessorArchitecture;
} SYSTEM_DATA;

Как только я вызываю GetSystemInfo , я просто «транспортирую» данные в новую структуру. Та же самая структура будет использоваться в управляемом коде. Учитывая, что методы правильно объявлены на собственной стороне и DLL компилируется правильно, управляемый фрагмент теперь будет состоять из трех частей. 

Объявление класса:

[ComImport, ClassInterface(ClassInterfaceType.None), Guid("8848DEB3-66B6-4AE3-A6F6-4A9C2F6595A5")]
public class CNativeAPI
{
}

Объявление интерфейса:

[ComImport, Guid("57C3A135-837D-486F-93E3-BF878F61892B"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface INativeAPI
{
    [return: MarshalAs(UnmanagedType.BStr)]
    string GetDeviceDriverInformation();

    void PlaySystemSound(uint sound);

    void ExitApplication();

    // Mainly CPU information.
    //**************************
    // ProcessorType
    //**************************
    // 2577 - STRONGARM
    //**************************
    // ProcessorLevel
    //**************************
    // 5 - ARMV5 (http://msdn.microsoft.com/en-us/library/ee488780.aspx)
    // Apparently the same applies to the OEM identificator.
    //**************************
    SYSTEM_DATA GetSystemInformation();
}

Объявление структуры для GetSystemInformation :

[StructLayout(LayoutKind.Sequential)]
public  struct SYSTEM_DATA 
{
    public uint PageSize;
    public uint ActiveProcessorMask;
    public uint NumberOfProcessors;
    public uint ProcessorType;
    public uint AllocationGranularity;
    public ushort ProcessorLevel;
    public ushort ProcessorRevision;
    public uint OEMID;
    public ushort ProcessorArchitecture;
}

Вы можете просто вызвать информационный вызов с помощью фрагмента кода, подобного следующему:

SYSTEM_DATA data = NativeAPIHook.GetSystemInformation();
MessageBox.Show(data.ProcessorArchitecture.ToString());

Поскольку значения, которые вы будете получать, являются целыми числами, вам необходимо найти их значение в официальной документации MSDN для Windows CE и Mobile, учитывая, что они являются константами и не требуют пояснений (например, NumberOfProcessors вернет 1).