在我的 Visual Studio 10 项目中使用资源时,我遇到了一个名为“Splash Screen”的构建操作,这让我找到了这篇关于WPF 的整洁启动屏幕功能的文章。
到处寻找,我发现这篇 MSDN 文章在 Windows Forms 中制作启动画面,但它是一种不同类型的方法:与其在应用程序加载之前使用本机代码加载启动画面,WinForms 版本只是在主窗体是时显示它初始化。
有没有办法在 WinForms 应用程序中实现这种高级类型的启动画面?
在我的 Visual Studio 10 项目中使用资源时,我遇到了一个名为“Splash Screen”的构建操作,这让我找到了这篇关于WPF 的整洁启动屏幕功能的文章。
到处寻找,我发现这篇 MSDN 文章在 Windows Forms 中制作启动画面,但它是一种不同类型的方法:与其在应用程序加载之前使用本机代码加载启动画面,WinForms 版本只是在主窗体是时显示它初始化。
有没有办法在 WinForms 应用程序中实现这种高级类型的启动画面?
是的。在与 .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);
}
}
可以为此使用非 WPF 表单,并且它工作得非常愉快。顺便说一句,它也是 VS2008 中的一个特性。