/// <summary> /// アイコンの情報を取得する /// (取得したアイコン情報のカラーBitmapハンドル(<see cref="IconInfo.ICONINFO.ColorBitmapHandle"/>) /// マスクBitmapハンドル(<see cref="IconInfo.ICONINFO.ColorBitmapHandle"/>)は /// <see cref="DeleteObject(IntPtr)"/> で破棄する必要がある) /// </summary> /// <param name="iconCursorHandle">アイコン/カーソルへのハンドル</param> /// <exception cref="PlatformInvokeException"> /// Win32Apiの処理「DLL:user32.dll、メソッド:GetIconInfo」の呼び出しに失敗した場合に発生 /// </exception> /// <exception cref="Win32OperateException"> /// Win32Apiの処理「DLL:user32.dll、メソッド:GetIconInfo」の処理に失敗した場合に発生 /// </exception> /// <returns>取得したアイコン情報</returns> private static IconInfo.ICONINFO GetIconInfo(SafeCopyIconHandle iconCursorHandle) { // Win32Apiの実行処理 // Win32ApiのWindou共通の呼び出し機能を用いて、アイコンの情報を取得処理を呼び出す Win32ApiResult Function() { bool win32Result = Win32Api.GetIconInfo(iconCursorHandle, out IconInfo.ICONINFO info); int win32ErrorCode = Marshal.GetLastWin32Error(); return(new Win32ApiResult(info, win32Result, win32ErrorCode)); } // 実行 string dllName = "user32.dll"; string methodName = nameof(Win32Api.GetIconInfo); Win32ApiResult result = Win32ApiCommon.Run(Function, dllName, methodName); // 正常終了したかチェック if (!result.Result && result.ErrorCode != (int)ErrorCode.NO_ERROR) { throw Win32ApiCommon.GetWin32OperateException(dllName, methodName, result.ErrorCode); } // 取得したアイコン情報を返却 return((IconInfo.ICONINFO)result.ReturnValue); }
/// <summary> /// 引数で指定された他のモジュールのアイコンへのハンドル(<paramref name="iconCursorHandle"/>)を /// 現在のモジュールのアイコンへのハンドルに複製する /// </summary> /// <remarks> /// この機能は、別のモジュールが所有しているアイコンを、現在のモジュールへの独自のハンドルで取得する /// 他のモジュールが解放されてもアプリケーションアイコンはアイコンとして使用することができる /// </remarks> /// <param name="iconCursorHandle">アイコン/カーソルへのハンドル</param> /// <exception cref="PlatformInvokeException"> /// Win32Apiの処理「DLL:user32.dll、メソッド:CopyIcon」の呼び出しに失敗した場合に発生 /// </exception> /// <exception cref="Win32OperateException"> /// Win32Apiの処理「DLL:user32.dll、メソッド:CopyIcon」の処理に失敗した場合に発生 /// </exception> /// <returns>複製したアイコンへのハンドル</returns> private static SafeCopyIconHandle CopyIcon(IntPtr iconCursorHandle) { // Win32Apiの実行処理 // Win32ApiのWindou共通の呼び出し機能を用いて、アイコンの複製処理を呼び出す Win32ApiResult Function() { SafeCopyIconHandle win32ReturnValue = Win32Api.CopyIcon(iconCursorHandle); int win32ErrorCode = Marshal.GetLastWin32Error(); bool win32Result = !Win32Api.CopyIconParameter.IsSuccess(win32ReturnValue); return(new Win32ApiResult(win32ReturnValue, win32Result, win32ErrorCode)); } // 実行 string dllName = "user32.dll"; string methodName = nameof(Win32Api.CopyIcon); Win32ApiResult result = Win32ApiCommon.Run(Function, dllName, methodName); // 正常終了したかチェック if (!result.Result && result.ErrorCode != (int)ErrorCode.NO_ERROR) { throw Win32ApiCommon.GetWin32OperateException(dllName, methodName, result.ErrorCode); } // 複製したアイコンへのハンドルを返却 return((SafeCopyIconHandle)result.ReturnValue); }
/// <summary> /// カーソルの画像を取得する /// </summary> /// <param name="iconHandle"> /// カーソルアイコンへのハンドル /// </param> /// <param name="iconInfo"> /// カーソルアイコン情報 /// </param> /// <param name="backgroundImage"> /// カーソルの背景画像(NULLを指定した場合は白一色の背景として描画する) /// </param> /// <param name="drawPoint"> /// 背景画像に対してカーソルを描画する座標 /// (背景画像を使用しない場合は NULL を指定する)</param> /// <exception cref="PlatformInvokeException"> /// Win32Apiの下記の処理の呼び出しに失敗した場合に発生 /// ・「DLL:gdi32.dll、メソッド:CreateCompatibleDC」 /// ・「DLL:gdi32.dll、メソッド:SelectObject」 /// ・「DLL:gdi32.dll、メソッド:BitBlt」 /// ・「DLL:gdi32.dll、メソッド:DeleteObject」 /// ・「DLL:gdi32.dll、メソッド:DeleteDC」 /// </exception> /// <exception cref="Win32OperateException"> /// Win32Apiの下記の処理に失敗した場合に発生 /// ・「DLL:gdi32.dll、メソッド:CreateCompatibleDC」 /// ・「DLL:gdi32.dll、メソッド:SelectObject」 /// ・「DLL:gdi32.dll、メソッド:BitBlt」 /// ・「DLL:gdi32.dll、メソッド:DeleteObject」 /// ・「DLL:gdi32.dll、メソッド:DeleteDC」 /// </exception> /// <returns>カーソル画像(取得できない場合はNULLを返却する)</returns> private static Bitmap GetCursorImage( SafeCopyIconHandle iconHandle, IconInfo.ICONINFO iconInfo, Bitmap backgroundImage = null, Point?drawPoint = null) { // カラー・マスク情報が存在するか判定 bool hasColor = IsBitmap(iconInfo.ColorBitmapHandle); bool hasMask = IsBitmap(iconInfo.MaskBitmapHandle); // アイコンのカラー、マスクの両方が取得できない場合 // 画像情報は取得できないため NULL を返す if (!hasColor && !hasMask) { return(null); } // アイコンのハンドルからカーソルアイコンを取得する Icon cursorIcon; try { cursorIcon = Icon.FromHandle(iconHandle.Handle); } catch (ExternalException) { // アイコンのハンドルからカーソルアイコンを生成できない場合、NULL を返す return(null); } // カーソルがモノクロかカラーでそれぞれ取得を行う if (hasColor) { // カラーの場合 // アイコン画像をビットマップ形式に変換して、そのまま返す return(cursorIcon.ToBitmap()); } // モノクロの場合 // 画像に関するリソースの解放用の宣言 Bitmap cursorImage = null; Graphics cursorImageGraphics = null; Bitmap baseImage = null; Bitmap maskImage = null; bool isCreateBase = false; try { // 各画像データの生成 // カーソル画像と、グラフィックオブジェクト生成 cursorImage = new Bitmap(cursorIcon.Width, cursorIcon.Height); cursorImageGraphics = Graphics.FromImage(cursorImage); // 背景画像の生成 // 引数で背景画像が与えられていない場合、白一色の背景画像を生成する if (backgroundImage == null) { baseImage = new Bitmap(cursorIcon.Width, cursorIcon.Height); using (Graphics graphics = Graphics.FromImage(baseImage)) { graphics.FillRectangle(Brushes.White, graphics.VisibleClipBounds); } isCreateBase = true; } else { isCreateBase = false; } // マスク画像の生成 maskImage = Image.FromHbitmap(iconInfo.MaskBitmapHandle); // アンマネージリソースの解放用の宣言 SafeDCHandle cursorHdc = null; SafeDCHandle baseHdc = null; SafeDCHandle maskHdc = null; IntPtr beforeBase = IntPtr.Zero; IntPtr beforeMask = IntPtr.Zero; RuntimeHelpers.PrepareConstrainedRegions(); try { // カーソル画像のデバイスコンテキストを取得 cursorHdc = new SafeDCHandle(cursorImageGraphics); // 背景画像のデバイスコンテキストを取得 IntPtr baseHBitmap = isCreateBase ? baseImage.GetHbitmap() : backgroundImage.GetHbitmap(); baseHdc = CreateCompatibleDC(IntPtr.Zero); beforeBase = SelectBitmap(baseHdc, baseHBitmap); // マスク画像のデバイスコンテキストを取得 IntPtr maskHBitmap = maskImage.GetHbitmap(); maskHdc = CreateCompatibleDC(IntPtr.Zero); beforeMask = SelectBitmap(maskHdc, maskHBitmap); // 画像の合成処理 int width = cursorImage.Width; int height = cursorImage.Height; Point base1Pt = drawPoint ?? new Point(0, 0); Point mask1Pt = new Point(0, 0); Point mask2Pt = new Point(0, maskImage.Height / 2); BitBlt(cursorHdc, 0, 0, width, height, baseHdc, base1Pt.X, base1Pt.Y, ROPCode.SRCCOPY); BitBlt(cursorHdc, 0, 0, width, height, maskHdc, mask1Pt.X, mask1Pt.Y, ROPCode.SRCAND); BitBlt(cursorHdc, 0, 0, width, height, maskHdc, mask2Pt.X, mask2Pt.Y, ROPCode.SRCINVERT); } finally { // リソースの解放処理 // カーソル画像に関するリソースの解放 try { } finally { cursorHdc.Dispose(); } // 元となる背景画像に関するリソースの解放 try { if (baseHdc != null) { IntPtr baseHandle = SelectBitmap(baseHdc, beforeBase); if (baseHandle != IntPtr.Zero) { DeleteObject(baseHandle); } } } finally { baseHdc?.Dispose(); } // マスク画像に関するリソースの解放 try { if (maskHdc != null) { IntPtr maskHandle = SelectBitmap(maskHdc, beforeMask); if (maskHandle != IntPtr.Zero) { DeleteObject(maskHandle); } } } finally { maskHdc?.Dispose(); } } } catch { // 例外発生時はカーソル画像を破棄する cursorImage.Dispose(); // 発生した例外はそのままスローする throw; } finally { // 画像リソースを解放する cursorImageGraphics?.Dispose(); baseImage?.Dispose(); maskImage?.Dispose(); } // 背景画像を白一色で生成した場合、背景を透過する if (isCreateBase) { cursorImage.MakeTransparent(Color.White); } // 合成したカーソル画像を返す return(cursorImage); }
/// <summary> /// 現在のカーソルをキャプチャする(画像、座標情報を取得) /// </summary> /// <param name="backgroundImage"> /// Iビームカーソル等の背景に応じで色が変化するカーソルを、 /// 正確に描画するために使用するカーソルの下にある背景画像 /// (NULLを指定した場合、白一色の背景としてカーソルをキャプチャする) /// </param> /// <param name="backgroundImageScreenPoint"> /// 背景画像の画面上の座標(画像の左上の絶対座標) /// </param> /// <exception cref="PlatformInvokeException"> /// Win32Apiの下記の処理の呼び出しに失敗した場合に発生 /// ・「DLL:user32.dll、メソッド:GetCursorInfo」 /// ・「DLL:user32.dll、メソッド:CopyIcon」 /// ・「DLL:user32.dll、メソッド:GetIconInfo」 /// ・「DLL:gdi32.dll、メソッド:CreateCompatibleDC」 /// ・「DLL:gdi32.dll、メソッド:SelectObject」 /// ・「DLL:gdi32.dll、メソッド:BitBlt」 /// ・「DLL:gdi32.dll、メソッド:DeleteObject」 /// ・「DLL:gdi32.dll、メソッド:DeleteDC」 /// ・「DLL:user32.dll、メソッド:DestroyIcon」 /// </exception> /// <exception cref="Win32OperateException"> /// Win32Apiの下記の処理に失敗した場合に発生 /// ・「DLL:user32.dll、メソッド:GetCursorInfo」 /// ・「DLL:user32.dll、メソッド:CopyIcon」 /// ・「DLL:user32.dll、メソッド:GetIconInfo」 /// ・「DLL:gdi32.dll、メソッド:CreateCompatibleDC」 /// ・「DLL:gdi32.dll、メソッド:SelectObject」 /// ・「DLL:gdi32.dll、メソッド:BitBlt」 /// ・「DLL:gdi32.dll、メソッド:DeleteObject」 /// ・「DLL:gdi32.dll、メソッド:DeleteDC」 /// ・「DLL:user32.dll、メソッド:DestroyIcon」 /// </exception> /// <returns> /// 現在のカーソルの画像、座標情報 /// (カーソルが取得できない場合はNULLを返却する) /// </returns> public static CursorInfo CaptureCurrentCursor( Bitmap backgroundImage, Point backgroundImageScreenPoint) { // カーソル情報を取得 Cursor.CURSORINFO cursorInfo = GetCursorInfo(); if (cursorInfo.Flag != (int)Cursor.State.CURSOR_SHOWING) { // カーソルを表示していない場合、NULL を返却する return(null); } // アイコン用のハンドルを宣言 SafeCopyIconHandle iconHandle = null; IconInfo.ICONINFO iconInfo = default; try { // システムからアイコンのハンドルをコピーしておく iconHandle = CopyIcon(cursorInfo.CursorHandle); // アイコン情報を取得 iconInfo = GetIconInfo(iconHandle); // 取得したアイコン情報がカーソルでない場合、NULL を返却する if (iconInfo.IsIcon) { return(null); } // カーソルの座標を取得 // 絶対座標(カーソルのホットスポットの分値を補正する) Point screenPoint = new Point( x: cursorInfo.ScreenPosition.X - iconInfo.HotspotX, y: cursorInfo.ScreenPosition.Y - iconInfo.HotspotY); // 背景画像からの相対座標 Point imagePoint = new Point( x: screenPoint.X - backgroundImageScreenPoint.X, y: screenPoint.Y - backgroundImageScreenPoint.Y); // カーソルの画像を取得 Bitmap cursorImage; if (backgroundImage != null) { cursorImage = GetCursorImage(iconHandle, iconInfo, backgroundImage, imagePoint); } else { cursorImage = GetCursorImage(iconHandle, iconInfo); } // カーソルの画像が取得できない場合は NULL を返す if (cursorImage == null) { return(null); } // カーソル情報を生成して返す return(new CursorInfo(cursorImage, screenPoint, imagePoint)); } finally { try { // アイコン情報のマスクと画像情報を破棄する if (!iconInfo.Equals(default(IconInfo.ICONINFO))) { DeleteObject(iconInfo.MaskBitmapHandle); DeleteObject(iconInfo.ColorBitmapHandle); } } finally { // アイコンを破棄する iconHandle?.Dispose(); } } }