Опубликован: 28.04.2009 | Доступ: свободный | Студентов: 1840 / 107 | Оценка: 4.36 / 4.40 | Длительность: 16:40:00
Специальности: Программист
Лекция 4:

Хранитель экрана

4.6. Визуализация в окне предварительного просмотра

Переходим к заключительному и самому нетривиальному этапу – отображению хранителя экрана в окне предварительного просмотра. Чтобы реализовать эту функциональность приложение должно получить из командной строки дескриптор области предварительного просмотра (мониторчик на вкладке Screen Saver окна Display Properties ) и создать в этом окне свой элемент управления. Для анимации созданного элемента управления окно Display Properties будет автоматически посылать ему сообщения WM_PAINT, а при выборе другого хранителя экрана, смене вкладки или закрытии диалогового окна Display Properties – сообщение WM_CLOSE.

К сожалению, большую часть этой функциональности не возможно реализовать средствами .NET Framework поэтому на придется опуститься до уровня оконных процедур и циклов обработки сообщений Win32. Для начала определим все необходимые константы, структуры и функции Win32 (листинг 4.21).

public static class Win32
{
...
// Определение делегата оконной функции обработки сообщений.
public delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
// Структура WNDCLASSEX, используемая при регистрации класса окна 
[StructLayout(LayoutKind.Sequential)] public struct WNDCLASSEX 
{
[MarshalAs(UnmanagedType.U4)]
public int cbSize;
[MarshalAs(UnmanagedType.U4)]
public uint style;
public WndProc lpfnWndProc;
public int cbClsExtra;
public int cbWndExtra;
public IntPtr hInstance;
public IntPtr hIcon;
public IntPtr hCursor;
public IntPtr hbrBackground;
public string lpszMenuName;
public string lpszClassName;
public IntPtr hIconSm; }
// Битовые флаги стилей окна [Flags]
public enum WindowStyles : uint 
{ 
// Окно является дочерним
WS_CHILD = 0x40000000, 
// Окно сразу же является видим
WS_VISIBLE = 0x10000000, 
// Окно игнорирует действия пользователя
WS_DISABLED = 0x08000000, 
...
}
// Битовые флаги стилей класса окна
[Flags]
public enum ClassStyles : uint
{ 
// Окно будет использовать контекст устройства родительского окна
CS_PARENTDC = 0x0080, 
...
}
// Идентификаторы сообщений Windows
public enum WindowsMessages : uint 
{
WM_CLOSE = 0x10, WM_DESTROY = 0x2, 
WM_PAINT = 0xF, 
...
}
// Регистрирует класс окна [DllImport("user32")] public static extern short 
RegisterClassEx([In] ref WNDCLASSEX lpwcx);
// Создает новое окно
[DllImport("user32.dll")]
public static extern IntPtr CreateWindowEx(uint dwExStyle, string lpClassName, 
4> string lpWindowName, uint dwStyle, int x, int y, int nWidth, int nHeight, 4> IntPtr
hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam);
// Возвращает координаты клиентской области окна [DllImport("user32.dll")]
public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
// Уничтожает окно
[DllImport("user32.dll")] public static extern bool
DestroyWindow(IntPtr hWnd);
// Помещает в очередь сообщений WMQUIT, завершающее выполнение цикла обработки сообщений
[DllImport("user32.dll")] public static extern void PostQuitMessage(int nExitCode);
// Вызывает обработчик сообщения по умолчанию
[DllImport("user32.dll")]
public static extern IntPtr DefWindowProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr
 lParam);
}
Листинг 4.21.

В функцию Main необходимо добавить код обработчика ключа /P, выполняющий следующие действия:

  1. Получение дескриптора окна предварительно просмотра.
  2. Определение размера окна предварительного просмотра.
  3. Регистрация класса окна и создание дочернего окна в окне предварительного просмотра.
  4. Создание экземпляра нашего класса Firework.
  5. Запуск цикла обработки сообщений.

Код, реализующий данную функциональность, приведен в листинге 4.22.

static void Main()
{
...
// Если приложение вызвано с ключом вида "/P n"
if ((args.Length == 3) && (args[1].ToUpper() == "/P"))
{ 
// Получаем дескриптор окна предварительного просмотра
IntPtr parentHandle = (IntPtr)uint.Parse(args[2]);
// Определяем координаты клиентской области окна предварительного просмотра 
Win32.RECT rect; Win32.GetClientRect(parentHandle, out rect);
// Создаем и заполняем структуру с информацией о классе окна
Win32.WNDCLASSEX wndClassEx = new Win32.WNDCLASSEX();
wndClassEx.cbSize = Marshal.SizeOf(wndClassEx);
wndClassEx.style = (uint)Win32.ClassStyles.CS_PARENTDC; 
// Указатель на оконную функцию (см. листинг 4.23).
wndClassEx.lpfnWndProc = new Win32.WndProc(WindowProc);
wndClassEx.cbClsExtra = 0;
wndClassEx.cbWndExtra = 0;
wndClassEx.hIcon = IntPtr.Zero;
wndClassEx.hIconSm = IntPtr.Zero;
wndClassEx.hCursor = IntPtr.Zero;
wndClassEx.hbrBackground = IntPtr.Zero;
wndClassEx.lpszMenuName = null;
wndClassEx.lpszClassName = "XNASCREENSAVER";
wndClassEx.hInstance = Marshal.GetHINSTANCE(typeof(Program) .Module);
// Регистрируем класс окна
if (Win32.RegisterClassEx(ref wndClassEx) == 0) 
return;
// Создаем дочернее окно для визуализации хранителя экрана
displayHandle = Win32.CreateWindowEx(0, "XNASCREENSAVER", "XNAScreenSaver",
(uint)(Win32.WindowStyles.WSCHILD | Win32.WindowStyles.WSVISIBLE |
Win32.WindowStyles.WSDISABLED), 0, 0, rect.Width, rect.Height, parentHandle, 4> IntPtr.Zero,
Marshal.GetHINSTANCE(typeof(Program).Module), IntPtr.Zero);
try 
{ 
// Создаем экземпляр класса Firework и передаем ему дескриптор созданного окна. Размер искр и 
// частота их появления подобраны таким образом, что фейерверк нормально смотрелся в 
// маленьком окошке предварительного просмотра.
firework = new Firework(displayHandle, 1.0f, 5); 
}
catch (FireworkException ex) 
{
MessageBox.Show(ex.Message, "Критическая ошибка", MessageBoxButtons.OK,
MessageBoxIcon.Error); return; 
}
// Запускаем цикл обработки сообщений. 
Application.Run(); return; 
}
return; 
}
}
Листинг 4.22.
Андрей Леонов
Андрей Леонов

Reference = add reference, в висуал студия 2010 не могу найти в вкладке Solution Explorer, Microsoft.Xna.Framework. Его нету.