1

在我的 Visual Studio 10 项目中使用资源时,我遇到了一个名为“Splash Screen”的构建操作,这让我找到了这篇关于WPF 的整洁启动屏幕功能的文章。

到处寻找,我发现这篇 MSDN 文章在 Windows Forms 中制作启动画面,但它是一种不同类型的方法:与其在应用程序加载之前使用本机代码加载启动画面,WinForms 版本只是在主窗体是时显示它初始化。

有没有办法在 WinForms 应用程序中实现这种高级类型的启动画面?

4

2 回答 2

4

是的。在与 .NET 3.5 SP1 打包之前,我为我们的 WPF 应用程序做了一个实现。

基本上,您在加载程序集和初始化应用程序时创建一个本机 Win32 窗口并显示 BMP 图像。您可以使用其他图像格式,但首选 BMP,因为它需要加载的库数量最少。

一个“创建原生启动窗口”的快速谷歌搜索了几篇文章。我发现的最全面的(通过快速浏览)是 Bradley Grainger:Displaying a Splash Screen with C++。这篇文章是为 WPF 写的,但是概念是一样的:创建一个原生窗口,启动你的应用程序,关闭窗口。

明天我将在工作中查看我的实施来源,并用示例更新我的答案。

在那之前,谷歌并研究已经存在的许多示例以开始使用。

更新

正如所承诺的,下面是实现启动画面的完整示例(有一点)。

using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Threading;
using System.Windows.Interop;

namespace SplashScreen
{

    public class SplashScreenManager
    {
        static SplashScreen _current = null;

        static SplashScreenManager() {}
        SplashScreenManager() { }

        public static SplashScreen Create(Module module, int resourceID)
        {
            if (_current != null)
            {
                _current.Close();
                _current.Dispose();
            }

            _current = new SplashScreen(module, resourceID);
            return _current;
        }

        public static void Close()
        {
            if (_current == null)
                return;

            _current.Close();
            _current.Dispose();
            _current = null;
        }

        public static SplashScreen Current
        {
            get { return _current; }
        }

    }

    public class SplashScreen : IDisposable
    {                
        static bool IsClassRegistered = false;
        static string WindowClassName = "SplashScreenWindowClass";

        IntPtr _bitmapHandle = IntPtr.Zero;
        int _bitmapHeight;
        int _bitmapWidth;
        bool _isClosed;

        UnsafeNativeMethods.WndProc _splashWindowProcedureCallback;

        IntPtr _windowHandle = IntPtr.Zero;

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        internal SplashScreen(Module module, int resourceID)
        {
            _bitmapHandle = UnsafeNativeMethods.LoadBitmap(Marshal.GetHINSTANCE(module), new IntPtr(resourceID));
            _splashWindowProcedureCallback = new UnsafeNativeMethods.WndProc(SplashWindowProcedure);
        }

        public void Close()
        {
            if (_isClosed)
                return;

            _isClosed = true;
            UnsafeNativeMethods.PostMessage(new HandleRef(this, _windowHandle), 0x10, IntPtr.Zero, IntPtr.Zero);
            if (_bitmapHandle != IntPtr.Zero)
            {
                UnsafeNativeMethods.DeleteObject(_bitmapHandle);
                _bitmapHandle = IntPtr.Zero;
            }
        }

        public void Close(IntPtr handle)
        {
            if (_windowHandle != IntPtr.Zero)
                UnsafeNativeMethods.SetForegroundWindow(handle);

            Close();
        }

        void CreateWindow()
        {
            if (!IsClassRegistered)
                RegisterClass();

            if (IsClassRegistered)
            {
                CreateWindowInternal();
                if (_windowHandle != IntPtr.Zero)
                    UnsafeNativeMethods.ShowWindow(_windowHandle, 5);

            }
        }

        public void Dispose()
        {
            Dispose(true);
        }

        protected virtual void Dispose(bool disposing)
        {
            Close(IntPtr.Zero);
            GC.SuppressFinalize(this);
        }

        void GetBitmapDimensions()
        {
            int cb = Marshal.SizeOf(typeof(UnsafeNativeMethods.BITMAP));
            IntPtr lpvObject = Marshal.AllocCoTaskMem(cb);
            UnsafeNativeMethods.GetObject(_bitmapHandle, cb, lpvObject);
            UnsafeNativeMethods.BITMAP bitmap = (UnsafeNativeMethods.BITMAP)Marshal.PtrToStructure(lpvObject, typeof(UnsafeNativeMethods.BITMAP));
            _bitmapWidth = bitmap.bmWidth;
            _bitmapHeight = bitmap.bmHeight;
            Marshal.FreeCoTaskMem(lpvObject);
        }

        void OnPaint(IntPtr hdc)
        {
            if (_bitmapHandle != IntPtr.Zero)
            {
                IntPtr ptr = UnsafeNativeMethods.CreateCompatibleDC(hdc);
                IntPtr hgdiobj = UnsafeNativeMethods.SelectObject(ptr, _bitmapHandle);
                UnsafeNativeMethods.BitBlt(hdc, 0, 0, _bitmapWidth, _bitmapHeight, ptr, 0, 0, 0xcc0020);
                UnsafeNativeMethods.SelectObject(ptr, hgdiobj);
                UnsafeNativeMethods.DeleteDC(ptr);
            }
        }

        void CreateWindowInternal()
        {
            int systemMetrics = UnsafeNativeMethods.GetSystemMetrics(0);
            int num4 = UnsafeNativeMethods.GetSystemMetrics(1);
            uint dwStyle = 0x80000000;
            uint dwExStyle = 0x188;
            IntPtr moduleHandle = UnsafeNativeMethods.GetModuleHandle(null);
            IntPtr desktopWindow = UnsafeNativeMethods.GetDesktopWindow();
            _windowHandle = UnsafeNativeMethods.CreateWindowEx(dwExStyle, WindowClassName, "", dwStyle, (systemMetrics - _bitmapWidth) / 2, (num4 - _bitmapHeight) / 2, _bitmapWidth, _bitmapHeight, desktopWindow, IntPtr.Zero, moduleHandle, IntPtr.Zero);
        }

        void RegisterClass()
        {
            IntPtr moduleHandle = UnsafeNativeMethods.GetModuleHandle(null);
            UnsafeNativeMethods.WNDCLASSEX lpwcx = new UnsafeNativeMethods.WNDCLASSEX();
            lpwcx.cbSize = (uint)Marshal.SizeOf(typeof(UnsafeNativeMethods.WNDCLASSEX));
            lpwcx.cbClsExtra = 0;
            lpwcx.cbWndExtra = 0;
            lpwcx.hbrBackground = IntPtr.Zero;
            lpwcx.hCursor = IntPtr.Zero;
            lpwcx.hIcon = IntPtr.Zero;
            lpwcx.hIconSm = IntPtr.Zero;
            lpwcx.hInstance = moduleHandle;
            lpwcx.lpfnWndProc = _splashWindowProcedureCallback;
            lpwcx.lpszClassName = WindowClassName;
            lpwcx.lpszMenuName = null;
            lpwcx.style = 0;
            if (UnsafeNativeMethods.RegisterClassExW(ref lpwcx) != 0)
            {
                IsClassRegistered = true;
            }
        }

        public void Show()
        {
            if (_windowHandle == IntPtr.Zero)
            {
                Thread thread = new Thread(new ThreadStart(ThreadMethod));
                thread.IsBackground = true;
                thread.Start();
            }
        }

        [SuppressUnmanagedCodeSecurity]
        IntPtr SplashWindowProcedure(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
        {
            if (msg == 15)
            {
                UnsafeNativeMethods.PAINTSTRUCT lpPaint = new UnsafeNativeMethods.PAINTSTRUCT();
                IntPtr hdc = UnsafeNativeMethods.BeginPaint(hWnd, out lpPaint);
                OnPaint(hdc);
                UnsafeNativeMethods.EndPaint(hWnd, ref lpPaint);
                return IntPtr.Zero;
            }

            return UnsafeNativeMethods.DefWindowProc(hWnd, msg, wParam, lParam);
        }

        [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
        void ThreadMethod()
        {
            if (_bitmapHandle != IntPtr.Zero)
            {
                GetBitmapDimensions();
                CreateWindow();
                MSG msg = new MSG();
                while (UnsafeNativeMethods.GetMessage(ref msg, _windowHandle, 0, 0) > 0)
                {
                    UnsafeNativeMethods.TranslateMessage(ref msg);
                    UnsafeNativeMethods.DispatchMessage(ref msg);
                }
                _windowHandle = IntPtr.Zero;
                GC.KeepAlive(this);
            }
        }

    }

    [SuppressUnmanagedCodeSecurity]
    internal sealed class UnsafeNativeMethods
    {
        // Fields
        internal const int GWL_EXSTYLE = -20;
        public const int LOGPIXELSX = 0x58;
        public const int LOGPIXELSY = 90;
        internal const uint MB_ICONASTERISK = 0x40;
        internal const uint MB_ICONERROR = 0x10;
        internal const uint MB_ICONEXCLAMATION = 0x30;
        internal const uint MB_ICONHAND = 0x10;
        internal const uint MB_ICONINFORMATION = 0x40;
        internal const uint MB_ICONQUESTION = 0x20;
        internal const uint MB_ICONWARNING = 0x30;
        internal const uint MB_OK = 0;
        internal const uint MB_OKCANCEL = 1;
        internal const uint MB_SETFOREGROUND = 0x10000;
        internal const uint MB_YESNO = 4;
        internal const uint MB_YESNOCANCEL = 3;
        internal const int SM_CXSCREEN = 0;
        internal const int SM_CYSCREEN = 1;
        public const int SPI_GETWORKAREA = 0x30;
        internal const uint SRCCOPY = 0xcc0020;
        public const int SW_HIDE = 0;
        internal const int SW_SHOW = 5;
        public const int SW_SHOWMAXIMIZED = 3;
        public const int SW_SHOWMINIMIZED = 2;
        public const int SW_SHOWNORMAL = 1;
        internal const int WM_CLOSE = 0x10;
        internal const uint WS_EX_TOOLWINDOW = 0x80;
        internal const uint WS_EX_TOPMOST = 8;
        internal const uint WS_EX_TRANSPARENT = 0x20;
        internal const uint WS_EX_WINDOWEDGE = 0x100;
        internal const uint WS_POPUP = 0x80000000;

        UnsafeNativeMethods() {}

        [DllImport("user32.dll")]
        public static extern IntPtr WindowFromPoint(POINT Point);
        [DllImport("user32.dll")]
        public static extern IntPtr ChildWindowFromPoint(IntPtr hWndParent, POINT Point);
        [DllImport("user32.dll")]
        public static extern bool ScreenToClient(IntPtr hWnd, ref POINT lpPoint);
        [DllImport("user32.dll")]
        internal static extern IntPtr BeginPaint(IntPtr hwnd, out PAINTSTRUCT lpPaint);
        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("gdi32.dll")]
        internal static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
        [DllImport("gdi32.dll")]
        internal static extern IntPtr CreateCompatibleDC(IntPtr hdc);
        [DllImport("user32.dll", EntryPoint = "CreateWindowExW", CharSet = CharSet.Unicode)]
        internal static extern IntPtr CreateWindowEx(uint dwExStyle, string lpClassName, string lpWindowName, uint dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam);
        [DllImport("user32.dll")]
        internal static extern IntPtr DefWindowProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam);
        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("gdi32.dll")]
        internal static extern bool DeleteDC(IntPtr hdc);
        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("gdi32.dll")]
        internal static extern bool DeleteObject(IntPtr hObject);
        [DllImport("user32.dll")]
        internal static extern IntPtr DispatchMessage([In] ref MSG lpmsg);
        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("user32.dll")]
        internal static extern bool EndPaint(IntPtr hWnd, ref PAINTSTRUCT lpPaint);
        [DllImport("user32.dll")]
        public static extern IntPtr GetDC(IntPtr hWnd);
        [DllImport("user32.dll")]
        internal static extern IntPtr GetDC(HandleRef hWnd);
        [DllImport("user32.dll")]
        internal static extern IntPtr GetDesktopWindow();
        [DllImport("gdi32.dll")]
        public static extern int GetDeviceCaps(IntPtr hDC, int index);
        [DllImport("user32.dll", EntryPoint = "GetMessageW", CharSet = CharSet.Unicode, ExactSpelling = true)]
        internal static extern int GetMessage([In, Out] ref MSG msg, IntPtr hWnd, int uMsgFilterMin, int uMsgFilterMax);
        [DllImport("kernel32.dll", EntryPoint = "GetModuleHandleW", CharSet = CharSet.Unicode)]
        internal static extern IntPtr GetModuleHandle(string lpModuleName);
        [DllImport("gdi32.dll")]
        internal static extern int GetObject(IntPtr hgdiobj, int cbBuffer, IntPtr lpvObject);
        [DllImport("user32.dll")]
        internal static extern int GetSystemMetrics(int nIndex);
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        internal static extern int GetWindowLong(IntPtr handle, int index);
        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("user32.dll")]
        public static extern bool GetWindowPlacement(IntPtr hWnd, out WINDOWPLACEMENT lpwndpl);
        [DllImport("user32.dll", EntryPoint = "LoadBitmapW", CharSet = CharSet.Unicode)]
        internal static extern IntPtr LoadBitmap(IntPtr hInstance, IntPtr lpBitmapName);
        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("user32.dll")]
        internal static extern bool PostMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
        [return: MarshalAs(UnmanagedType.U2)]
        [DllImport("user32.dll")]
        internal static extern short RegisterClassExW([In] ref WNDCLASSEX lpwcx);
        [DllImport("user32.dll")]
        public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
        [DllImport("gdi32.dll")]
        internal static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("user32.dll")]
        internal static extern bool SetForegroundWindow(IntPtr hWnd);
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        internal static extern int SetWindowLong(IntPtr handle, int index, int dwNewLong);
        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("user32.dll")]
        public static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);
        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("user32.dll")]
        internal static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("user32.dll")]
        public static extern bool SystemParametersInfo(int nAction, int nParam, ref RECT rc, int nUpdate);
        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("user32.dll")]
        internal static extern bool TranslateMessage([In] ref MSG lpMsg);

        [StructLayout(LayoutKind.Sequential)]
        internal struct BITMAP
        {
            public int bmType;
            public int bmWidth;
            public int bmHeight;
            public int bmWidthBytes;
            public ushort bmPlanes;
            public ushort bmBitsPixel;
            public IntPtr bmBits;
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct PAINTSTRUCT
        {
            public IntPtr hdc;
            public bool fErase;
            public UnsafeNativeMethods.RECT rcPaint;
            public bool fRestore;
            public bool fIncUpdate;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)]
            public byte[] rgbReserved;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            public int X;
            public int Y;
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct RECT
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct WINDOWPLACEMENT
        {
            public int Length;
            public int Flags;
            public int ShowCmd;
            public UnsafeNativeMethods.POINT MinPosition;
            public UnsafeNativeMethods.POINT MaxPosition;
            public UnsafeNativeMethods.RECT NormalPosition;
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        internal struct WNDCLASSEX
        {
            public uint cbSize;
            public uint style;
            public UnsafeNativeMethods.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;
        }

        internal delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
    }

}
于 2010-06-29T12:30:44.423 回答
0

可以为此使用非 WPF 表单,并且它工作得非常愉快。顺便说一句,它也是 VS2008 中的一个特性。

于 2010-06-29T12:20:29.820 回答