Reference = add reference, в висуал студия 2010 не могу найти в вкладке Solution Explorer, Microsoft.Xna.Framework. Его нету. |
Хранитель экрана
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, выполняющий следующие действия:
- Получение дескриптора окна предварительно просмотра.
- Определение размера окна предварительного просмотра.
- Регистрация класса окна и создание дочернего окна в окне предварительного просмотра.
- Создание экземпляра нашего класса Firework.
- Запуск цикла обработки сообщений.
Код, реализующий данную функциональность, приведен в листинге 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.