3

問題:このコードをループで 3322 回 (bottom メソッドを使用すると 1246 回) 使用した後、GetHIcon() で一般的な GDI+ 例外がスローされます。

サンプル プロジェクト: http://dl.dropbox.com/u/18919663/TestGDICursorDrawing.zip

私がやろうとしていること:ループ内のビットマップから新しいカーソルを描画して、単純なフォーカス アニメーションを実行します。

既に確認したこと:すべてのビットマップとグラフィックスが破棄されていることを確認し、メモリ リークを監視して確認しました。また、他のプロセスに目に見えるリークがないことも確認してください。ビットマップが正しく使用されていることを確認するために、別の方法と方法を試しました。

Google からの回答: GDI+ にバグがあるようで、誰も解決策を提供していません。1 人が独自のビットマップからアイコンへのコンバーターを作成しようとしましたが、非一般的な画像サイズを処理するには柔軟性がありません。

public static Cursor CreateCursor(Bitmap bmp, int xHotSpot, int yHotSpot)
{
    //Shows me exactly when the error occurs.
    counter++;
    Console.WriteLine(counter + " GetHicon() calls");

    //GetHicon() is the trouble maker. 
    var newCur = new Cursor(bmp.GetHicon());
    bmp.Dispose();
    bmp = null;

    return newCur;
}

私が試した他の方法:

public static Cursor CreateCursor(Bitmap bmp, int xHotSpot, int yHotSpot)
{
    //Tried this method too, but this method results in an error with even fewer loops.
    Bitmap newBitmap = new Bitmap(bmp);
    // was told to try to make a new bitmap and dispose of the last to ensure that it wasn't locked or being used somewhere. 
    bmp.Dispose();
    bmp = null;
    //error occurs here. 
    IntPtr ptr = newBitmap.GetHicon();
    ICONINFO tmp = new ICONINFO();
    GetIconInfo(ptr, ref tmp);
    tmp.xHotspot = xHotSpot;
    tmp.yHotspot = yHotSpot;
    tmp.fIcon = false;
    ptr = CreateIconIndirect(ref tmp);

    newBitmap.Dispose();
    newBitmap = null;

    return new Cursor(ptr);
}


[DllImport("user32.dll", EntryPoint = "GetIconInfo")]
public static extern bool GetIconInfo(IntPtr hIcon, ref ICONINFO piconinfo);

[DllImport("user32.dll")]
public static extern IntPtr CreateIconIndirect(ref ICONINFO icon);

[StructLayout(LayoutKind.Sequential)]
public struct ICONINFO
{
    public bool fIcon;         // Specifies whether this structure defines an icon or a cursor. A value of TRUE specifies 
    public Int32 xHotspot;     // Specifies the x-coordinate of a cursor's hot spot. If this structure defines an icon, the hot 
    public Int32 yHotspot;     // Specifies the y-coordinate of the cursor's hot spot. If this structure defines an icon, the hot 
    public IntPtr hbmMask;     // (HBITMAP) Specifies the icon bitmask bitmap. If this structure defines a black and white icon, 
    public IntPtr hbmColor;    // (HBITMAP) Handle to the icon color bitmap. This member can be optional if this 
}
4

2 に答える 2

6

この問題は、その症状を考えると、間違いなくメモリ リークのように見えます。しばらくは正常に動作しますが、すぐに爆発します。

結局のところ、あなたが試した 2 番目の方法は、GDI オブジェクトのリークがひどいものでした。を呼び出して構造体GetIconInfoを埋めるICONINFOと、実際にはアイコン/カーソルに対応する 2 つのビットマップが作成されhbmMaskhbmColor. それらを使い終わったら、これらを削除するために呼び出す必要があります。そうしないと、リークします。ドキュメントDeleteObjectの備考セクションによると:

GetIconInfohbmMaskおよびhbmColorのメンバーのビットマップを作成しますICONINFO。呼び出し元のアプリケーションは、これらのビットマップを管理し、不要になったら削除する必要があります。

ここでのリークはこれだけではありません。どちらの方法でも、少なくとも 2 つの追加のリークが見られます。

  • このメソッドでは、アイコンの使用が終了したらBitmap.GetHicon呼び出す必要があります。DestroyIconあなたもそれをしていないので、毎回そのアイコンを漏らしています。

  • Bitmap作成している、GraphicsGraphicsPath、およびCursorオブジェクトを最後の最後まで破棄しません。つまりwhile、反復DrawRingAroundCursorごとに作成されたすべての一時オブジェクトがリークされます。(GDI+ オブジェクトの作成をusingステートメントでラップすることをお勧めしDisposeます。メソッドの呼び出しを忘れないようにすることをお勧めします。)

これらのリークをすべて修正すると、実行速度が 2 倍以上になり、同心円が表示されなくなります。しかし、クラッシュせずに無期限に実行することはまだできないので、まだ見つかっていないリークがいくつかあるはずです.

Thread.Sleepまた、危険信号を発したり、頭の中で大きな警告ベルを鳴らしたりするようなものもあります。

ここで、別のデザインを試すことを強くお勧めします。これらのオブジェクトをすべて作成すると、それらの有効期間を適切に管理していたとしても、比較的コストがかかり、不要に思えます。さらに、ユーザーがアプリケーションのウィンドウの外で他のオブジェクトの上にカーソルを移動するとすぐに、Windows はWM_SETCURSORホバーされた新しいウィンドウに新しいメッセージを送信し、カーソルをまったく別のものに変更します。最小限の労力でこの効果を「なくす」ことは非常に簡単です。

于 2012-05-11T19:56:59.033 に答える
2

メモリ リークを防ぐために、GetHicon の後に DestroyIcon を使用してください。

[DllImport("user32.dll", CharSet = CharSet.Auto)]
extern static bool DestroyIcon(IntPtr handle);

MSDN : https://msdn.microsoft.com/en-us/library/system.drawing.bitmap.gethicon%28v=vs.110%29.aspx

私のコードサンプル:

 [DllImport("user32.dll", CharSet = CharSet.Auto)]
 extern static bool DestroyIcon(IntPtr handle);
 public static Icon ConvertoToIcon(Bitmap bmp)
 {
     System.IntPtr icH = bmp.GetHicon();
     var toReturn = (Icon)Icon.FromHandle(icH).Clone();
     DestroyIcon(icH);
     return toReturn;
 }
于 2015-07-21T10:47:04.440 に答える