Visual Studio 10 プロジェクトでリソースをいじっていたときに、「スプラッシュ スクリーン」と呼ばれるビルド アクションに出会い、WPF の優れたスプラッシュ スクリーン機能に関するこの記事を見つけました。
Windows フォームでスプラッシュ スクリーンを作成することに関するこの MSDN の記事を見つけましたが、それは別の種類のアプローチです。初期化中。
WinForms アプリでこの優れたタイプのスプラッシュ スクリーンを実現する方法はありますか?
Visual Studio 10 プロジェクトでリソースをいじっていたときに、「スプラッシュ スクリーン」と呼ばれるビルド アクションに出会い、WPF の優れたスプラッシュ スクリーン機能に関するこの記事を見つけました。
Windows フォームでスプラッシュ スクリーンを作成することに関するこの MSDN の記事を見つけましたが、それは別の種類のアプローチです。初期化中。
WinForms アプリでこの優れたタイプのスプラッシュ スクリーンを実現する方法はありますか?
はい。.NET 3.5 SP1 にパッケージ化される前に、WPF アプリケーションの実装を行いました。
基本的に、ネイティブ Win32 ウィンドウを作成し、アセンブリをロードしてアプリケーションを初期化している間に BMP イメージを表示します。他の画像形式を使用することもできますが、必要なライブラリの数が最も少ないため、BMP が推奨されます。
「ネイティブ スプラッシュ ウィンドウの作成」を Google で検索すると、いくつかの記事が表示されました。私が見つけた最も包括的なもの (クイック スキャンから) は、Bradley Grainger: Displaying a Splash Screen with C++によるものでした。この記事は WPF を念頭に置いて書かれていますが、概念は同じです: ネイティブ ウィンドウを作成し、アプリケーションを起動し、ウィンドウを閉じます。
明日の職場での実装のソースを見て、例で回答を更新します。
それまでは、Google で検索して、すでに出回っている多くの例を調べて開始してください。
アップデート
約束どおり、以下はスプラッシュ スクリーンを実装する完全な例です (少しだけあります)。
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 の機能でもありました。