C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

動的にカーソルアイコンを生成する方法

[トピック内 8 記事 (1 - 8 表示)]  << 0 >>

■100781 / inTopicNo.1)  動的にカーソルアイコンを生成する方法
  
□投稿者/ クレ (1回)-(2022/10/26(Wed) 18:55:49)

分類:[.NET 全般] 


VB.NETを使って、
動的にカーソルアイコンを生成するプログラムを書いています。


以下のようなコードでうまく動作することを確認できています。



    Dim Cursor_Image As Windows.Forms.Cursor

    Private Sub PictureBox2_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox2.MouseMove


        Using bmp As New Bitmap(1, 1)
		
		ここでカーソルを生成

            If Cursor_Image IsNot Nothing Then

                DestroyIcon(Cursor_Image.Handle)
                Cursor_Image.Dispose()
                Cursor_Image = Nothing

            End If


            Dim hicon As IntPtr = bmp.GetHicon()

            Dim info As New ICONINFO()
            GetIconInfo(hicon, info)
            info.xHotspot = bmp.Width \ 2
            info.yHotspot = bmp.Height \ 2
            info.fIcon = CInt(False)


            DestroyIcon(hicon)
            hicon = Nothing


            Dim hicon2 = CreateIconIndirect(info)


            Cursor_Image = New System.Windows.Forms.Cursor(hicon2)
            PictureBox2.Cursor = Cursor_Image

        End Using

    End Sub


http://raizo.blog21.fc2.com/blog-entry-319.html

このページにあるように、
DestroyIconでアイコンを廃棄せずに
何度も実行するとbmp.GetHicon()でエラーが発生してしまうことが分かっています。

そのため、カーソルを共有変数にした上で毎回廃棄するようにしてみたのですが、
数千回くらい実行すると
bmp.GetHicon()でエラーが発生してしまいます。



上記のコードではカーソルのホットスポットを変更するために
二段階に分けてカーソルのハンドルを生成していますが、
以下のように一段階で生成するようにするとなぜかエラーが出なくなります。


                        Dim hicon As IntPtr = bmp.GetHicon()
                        Cursor_Circle = New System.Windows.Forms.Cursor(hicon)
                        Pbox.Cursor = Cursor_Circle

どのようにすれば、二段階でのカーソル生成で
エラーが出なくなりますか?

引用返信 編集キー/
■100782 / inTopicNo.2)  Re[1]: 動的にカーソルアイコンを生成する方法
□投稿者/ KOZ (335回)-(2022/10/26(Wed) 20:33:03)
2022/10/26(Wed) 20:41:07 編集(投稿者)
No100781 (クレ さん) に返信
> 		ここでカーソルを生成
> 
>             If Cursor_Image IsNot Nothing Then
> 
>                 DestroyIcon(Cursor_Image.Handle)
>                 Cursor_Image.Dispose()
>                 Cursor_Image = Nothing
> 
>             End If

DestroyIcon の戻り値を確認してください。
おそらく False が返ってきてると思われます。というのは

>             Dim info As New ICONINFO()
>             GetIconInfo(hicon, info)
>             info.xHotspot = bmp.Width \ 2
>             info.yHotspot = bmp.Height \ 2
>             info.fIcon = CInt(False)

ICONINFO.fIcon に False を渡すと、カーソルが作られるからです。
(カーソルの削除は DestroyCursor)

なので、

>             info.fIcon = CInt(False)

この行を消せばよい気がします。
(あるいは、DestroyCursor(Cursor_Image.Handle) とする)

API の宣言が不具合の原因になっている可能性もあるので
コードを投稿するときは、宣言部分も書きましょう。

引用返信 編集キー/
■100783 / inTopicNo.3)  Re[2]: 動的にカーソルアイコンを生成する方法
□投稿者/ クレ (2回)-(2022/10/26(Wed) 21:10:14)
ありがとうございます。

APIの宣言は以下のようになっております。



<System.Runtime.InteropServices.DllImport("user32")>
Private Shared Function DestroyIcon(ByVal hIcon As IntPtr) As Boolean
End Function

<System.Runtime.InteropServices.DllImport("user32")>
Private Shared Function DestroyCursor(ByVal hIcon As IntPtr) As Boolean
End Function




教えてくださった通り、

info.fIcon = CInt(False)を削除した状態でDestroyIconを使用、


info.fIcon = CInt(False)を残した状態でDestroyCursorを使用、

の二通りを試してみました。

しかし、いずれもやはり、3000回程度実行するとフリーズしてしまいます。


DestroyIconやDestroyCursorの返り値を見てみましたが、
仰る通り、info.fIconに連動してTrueやFalseになることは確認できました。

しかし、毎回Trueを返すような状態にしても
やはり何度も実行すると
Dim hicon As IntPtr = bmp.GetHicon()

が実行できなくなります。


Dim hicon As IntPtr = bmp.GetHicon()
Cursor_Circle = New System.Windows.Forms.Cursor(hicon)
Pbox.Cursor = Cursor_Circle

のようにして、一段階で生成する方法を使うと
DestroyIconでもDestroyCursorでも両方とも
3000回程度ならフリーズすることなく実行できます。


そのため、今回のフリーズする問題と
iconやcursorの識別は別ではないかと思います。


何が原因でしょうか・・・?






引用返信 編集キー/
■100784 / inTopicNo.4)  Re[2]: 動的にカーソルアイコンを生成する方法
□投稿者/ KOZ (336回)-(2022/10/26(Wed) 21:45:31)
No100782 (KOZ) に返信

ちょっとやってみたのですが、予想と違っていました。ごめんなさい。
DestroyIcon でも問題なく消せます。

問題があるのは、ICONINFO 構造体のほうにビットマップオブジェクトが存在しているためのようです。

info.hbmMask
info.hbmColor

を使い終わったら、DeleteObject してください。



引用返信 編集キー/
■100785 / inTopicNo.5)  Re[3]: 動的にカーソルアイコンを生成する方法
□投稿者/ クレ (3回)-(2022/10/26(Wed) 22:12:16)
ありがとうございます。


うまくいきました。
こんな情報、検索しても見つかりませんでした・・・
解決済み
引用返信 編集キー/
■100786 / inTopicNo.6)  Re[4]: 動的にカーソルアイコンを生成する方法
□投稿者/ KOZ (337回)-(2022/10/26(Wed) 22:46:30)
No100785 (クレ さん) に返信
> こんな情報、検索しても見つかりませんでした・・・

GetIconInfo の説明にあります。
# 私も気づいていませんでした。lol

「GetIconInfo function (winuser.h)」
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-geticoninfo

Remarks
GetIconInfo creates bitmaps for the hbmMask and hbmCol or members of ICONINFO.
The calling application must manage these bitmaps and delete them when they are no longer necessary.

解決済み
引用返信 編集キー/
■100797 / inTopicNo.7)  Re[5]: 動的にカーソルアイコンを生成する方法
□投稿者/ KOZ (340回)-(2022/10/27(Thu) 17:35:58)
No100786 (KOZ) に返信

完成したコードが無いのも残念なので、投下しておきます。
GetIconInfo で取得した ICONINFO 構造体(Class として宣言)を Cursor.Tag プロパティに保持しておき、不要になったら削除します。

Imports System.Runtime.InteropServices

Public Class Form1

    Private Cursor_Image As Cursor

    Private Sub PictureBox1_MouseMove(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseMove
        DestroyCursor(Cursor_Image)
        Using bmp As New Bitmap(16, 16)
            Using g As Graphics = Graphics.FromImage(bmp)
                g.DrawLine(Pens.Red, 0, 0, 0, 5)
                g.DrawLine(Pens.Red, 0, 0, 5, 0)
                g.DrawLine(Pens.Red, 0, 0, 15, 15)
            End Using
            Cursor_Image = CreateCursor(bmp, 0, 0)
            PictureBox1.Cursor = Cursor_Image
        End Using
    End Sub

    Protected Overrides Sub OnFormClosed(e As FormClosedEventArgs)
        MyBase.OnFormClosed(e)
        DestroyCursor(Cursor_Image)
    End Sub

    Private Shared Function CreateCursor(bmp As Bitmap, x As Integer, y As Integer) As Cursor
        Dim hIcon As IntPtr = bmp.GetHicon()
        Dim info As New ICONINFO()
        GetIconInfo(hIcon, info)
        DestroyIcon(hIcon)
        info.xHotspot = x
        info.yHotspot = y
        info.fIcon = 0
        Dim hCursor As IntPtr = CreateIconIndirect(info)
        Dim cursor = New Cursor(hCursor) With {.Tag = info}
        Return cursor
    End Function

    Private Shared Sub DestroyCursor(ByRef cursor As Cursor)
        If cursor IsNot Nothing Then
            DestroyIcon(cursor.Handle)
            Dim info As ICONINFO = TryCast(cursor.Tag, ICONINFO)
            If info IsNot Nothing Then
                DeleteObject(info.hbmColor)
                DeleteObject(info.hbmMask)
            End If
            cursor.Dispose()
            cursor = Nothing
        End If
    End Sub

    <StructLayout(LayoutKind.Sequential)>
    Private Class ICONINFO
        Public fIcon As Integer
        Public xHotspot As Integer
        Public yHotspot As Integer
        Public hbmMask As IntPtr
        Public hbmColor As IntPtr
    End Class

    <DllImport("user32.dll")>
    Private Shared Function GetIconInfo(hIcon As IntPtr, iconinfo As ICONINFO) As Boolean
    End Function

    <DllImport("user32.dll")>
    Private Shared Function CreateIconIndirect(iconInfo As ICONINFO) As IntPtr
    End Function

    <DllImport("user32.dll")>
    Private Shared Function DestroyIcon(hIcon As IntPtr) As Boolean
    End Function

    <DllImport("gdi32.dll")>
    Private Shared Function DeleteObject(hObject As IntPtr) As Boolean
    End Function

End Class

解決済み
引用返信 編集キー/
■100820 / inTopicNo.8)  Re[6]: 動的にカーソルアイコンを生成する方法
□投稿者/ kure (1回)-(2022/10/30(Sun) 16:12:43)
2022/10/30(Sun) 16:13:14 編集(投稿者)

わざわざコードまで書いていただきありがとうございました。

解決済み
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ