none
System.Drawing.Graphics.ClipBounds で ArgumentException RRS feed

  • 質問

  • お世話になります。
    DataGridViewを使ったプログラムのメモリリークを調査していた所、Graphics.ClipBoundsでArgumentExceptionが発生しているのを見つけました。
    > System.Drawing.dll!System.Drawing.Graphics.ClipBounds.get() 行 4134 C#

    Graphics.csを確認した所、次のようになっていました。
    --------
            /// <include file='doc\Graphics.uex' path='docs/doc[@for="Graphics.ClipBounds"]/*' />
            /// <devdoc>
            /// </devdoc>
            public RectangleF ClipBounds {
                get {
                    GPRECTF rect = new GPRECTF();   ←ここで例外

                    int status = SafeNativeMethods.Gdip.GdipGetClipBounds(new HandleRef(this, this.NativeGraphics), ref rect);

                    if (status != SafeNativeMethods.Gdip.Ok) {
                        throw SafeNativeMethods.Gdip.StatusException(status);
                    }

                    return rect.ToRectangleF();
                }
            }
    --------

    この GPRECTFのソースも見たのですが、引数なしのコンストラクタがありません。
    http://referencesource.microsoft.com/#System.Drawing/commonui/System/Drawing/Advanced/GPRECTF.cs

    実害はないようですが、メモリリークの要因かもしれないので気になっています。
    何か関連情報をお持ちの方が見えましたら、お教えください。

    2015年6月16日 6:11

回答

  • 勘違いをされているようなので…

    基本的に例外は何もないところから発生することはありません。ArgumentExceptionのインスタンスがnewされそのコンストラクタ引数に従ってMessageプロパティが設定されます。その後、throw文で例外が投げられるわけですが、その際にStackTraceプロパティが設定されます。

    ですので、まずStackTraceプロパティに書かれているメソッドにthrow文があります。メソッド内にthrow文が複数あった場合はMessageなど他のプロパティから推測できます。

    # 基本的に、でありExecutionEngineExceptionやStackOverflowExceptionなどなどランタイムが投げる特殊な例外もありますが…。

    今回の場合、私の推測が正しければ throw SafeNativeMethods.Gdip.StatusException(status); で投げられており、それはソースコードからも分かるように SafeNativeMethods.Gdip.GdipGetClipBounds(new HandleRef(this, this.NativeGraphics), ref rect); がOk以外のステータスを返したから、と思います。

    • 回答の候補に設定 佐祐理 2015年6月17日 0:50
    • 回答としてマーク まさゆき 2015年6月17日 1:29
    2015年6月16日 9:00
  • つまり実際は SafeNativeMethods.Gdip.StatusException の throw 処理中に ArgumentException  が発生したため、結果として ArgumentException  に見えているという理解で良いのでしょうか。
    理解力に乏しくですみません。

    GDI+ の内部の関数が「失敗」を返したので、StatusException メソッドが対応する例外に変換して throw しただけです。
    GdipGetClipBounds が失敗するわけですし、Graphics の状態がおかしいか、呼び出しスレッドがおかしいかなど、周りのコードの問題だと思われます。
    • 回答としてマーク まさゆき 2015年6月17日 0:27
    2015年6月16日 13:44
    モデレータ

すべての返信

  • こんにちは。

    引数なしのコンストラクタが無いのは構造体だからだと思います。
    メモリリークが起きた時にArgumentExceptionが起きるのかどうかはわからなかったですが…。
    メモリリークが起きるときにGPRECTFの領域を確保しようとして例外がはかれているのかもしれません。

    いずれにせよ該当箇所がメモリリークの直接的な要因とはならないのではないかと私は思います。

    #「かもしれない」「思います」ばかりですみません。

    2015年6月16日 6:53
    モデレータ
  • referencesourceで閲覧できるソースコードと実際に.NET Frameworkのコンパイルに使用されたソースコードは完全には同一ではありません。ですので行番号から発生個所を特定するのは危険です。throw SafeNativeMethods.Gdip.StatusException(status); 側で発生した例外だったりしませんか?
    • 回答の候補に設定 佐祐理 2015年6月17日 0:50
    2015年6月16日 7:24
  • ありがとうございます。
    試しに呼び元でメソッドを呼ばないようにして、例外を発生させないようにしてもメモリリークに変化はないようでした。
    どうも関連はなさそうです。

    2015年6月16日 8:22
  • ありがとうございます。
    VS2013のデバッガでステップ実行したら、上記の箇所でした。
    確かに完全に同一という保証はないですね。
    ArgumentExceptionだと、
    -----
    int status = SafeNativeMethods.Gdip.GdipGetClipBounds(new HandleRef(this, this.NativeGraphics), ref rect);
    -----
    の方も怪しいかもしれません。

    2015年6月16日 8:30
  • 勘違いをされているようなので…

    基本的に例外は何もないところから発生することはありません。ArgumentExceptionのインスタンスがnewされそのコンストラクタ引数に従ってMessageプロパティが設定されます。その後、throw文で例外が投げられるわけですが、その際にStackTraceプロパティが設定されます。

    ですので、まずStackTraceプロパティに書かれているメソッドにthrow文があります。メソッド内にthrow文が複数あった場合はMessageなど他のプロパティから推測できます。

    # 基本的に、でありExecutionEngineExceptionやStackOverflowExceptionなどなどランタイムが投げる特殊な例外もありますが…。

    今回の場合、私の推測が正しければ throw SafeNativeMethods.Gdip.StatusException(status); で投げられており、それはソースコードからも分かるように SafeNativeMethods.Gdip.GdipGetClipBounds(new HandleRef(this, this.NativeGraphics), ref rect); がOk以外のステータスを返したから、と思います。

    • 回答の候補に設定 佐祐理 2015年6月17日 0:50
    • 回答としてマーク まさゆき 2015年6月17日 1:29
    2015年6月16日 9:00
  • つまり実際は SafeNativeMethods.Gdip.StatusException の throw 処理中に ArgumentException  が発生したため、結果として ArgumentException  に見えているという理解で良いのでしょうか。
    理解力に乏しくですみません。

    2015年6月16日 12:21
  • つまり実際は SafeNativeMethods.Gdip.StatusException の throw 処理中に ArgumentException  が発生したため、結果として ArgumentException  に見えているという理解で良いのでしょうか。
    理解力に乏しくですみません。

    GDI+ の内部の関数が「失敗」を返したので、StatusException メソッドが対応する例外に変換して throw しただけです。
    GdipGetClipBounds が失敗するわけですし、Graphics の状態がおかしいか、呼び出しスレッドがおかしいかなど、周りのコードの問題だと思われます。
    • 回答としてマーク まさゆき 2015年6月17日 0:27
    2015年6月16日 13:44
    モデレータ
  • だから何もないところからArgumentExceptionが発生することはありません。再度説明しますがnew ArgumentExceptionされたからこそArgumentExceptionが投げられるわけです。

    SafeNativeMethods.Gdip.StatusException()はいわゆるexception helper関数であり、statusに応じたExceptionを返す関数であり、statusがInvalidParameterかUnknownImageFormatかPropertyNotFoundかPropertyNotSupportedかFontFamilyNotFoundかFontStyleNotFoundかNotTrueTypeFontの可能性があります。

    • 回答の候補に設定 佐祐理 2015年6月17日 0:50
    2015年6月16日 23:00
  • あれから他の部分も調べて、Dispose関連の問題をいくつか見つけました。
    対処した結果、全体としてのメモリーリークは相変わらずですが、上記の ArgumentException 自体が発生しなくなりました。
    ありがとうございました。

    2015年6月17日 0:09
  • >だから何もないところからArgumentExceptionが発生することはありません。
    それはわかっています。
    わからないのは、誰が new ArgumentException したかでした。
    SafeNativeMethods.Gdip.StatusException(status) のソースを確認したら、ArgumentExceptionも投げてました。
    -----
        internal static Exception StatusException(int status) {

            Debug.Assert(status != Ok, "Throwing an exception for an 'Ok' return code");

            switch (status) {
                case GenericError:       return new ExternalException(SR.GetString(SR.GdiplusGenericError), E_FAIL);
                case InvalidParameter:   return new ArgumentException(SR.GetString(SR.GdiplusInvalidParameter));
                case OutOfMemory:        return new OutOfMemoryException(SR.GetString(SR.GdiplusOutOfMemory));
                case ObjectBusy:         return new InvalidOperationException(SR.GetString(SR.GdiplusObjectBusy));
                case InsufficientBuffer: return new OutOfMemoryException(SR.GetString(SR.GdiplusInsufficientBuffer));
                case NotImplemented:     return new NotImplementedException(SR.GetString(SR.GdiplusNotImplemented));
                case Win32Error:         return new ExternalException(SR.GetString(SR.GdiplusGenericError), E_FAIL);
                case WrongState:         return new InvalidOperationException(SR.GetString(SR.GdiplusWrongState));
                case Aborted:            return new ExternalException(SR.GetString(SR.GdiplusAborted), E_ABORT);
                case FileNotFound:       return new FileNotFoundException(SR.GetString(SR.GdiplusFileNotFound));
                case ValueOverflow:      return new OverflowException(SR.GetString(SR.GdiplusOverflow));
                case AccessDenied:       return new ExternalException(SR.GetString(SR.GdiplusAccessDenied), E_ACCESSDENIED);
                case UnknownImageFormat: return new ArgumentException(SR.GetString(SR.GdiplusUnknownImageFormat));
                case PropertyNotFound:   return new ArgumentException(SR.GetString(SR.GdiplusPropertyNotFoundError));
                case PropertyNotSupported:return new ArgumentException(SR.GetString(SR.GdiplusPropertyNotSupportedError));
    -----
    ということで、佐祐理様の言われたとおりでした。
    ありがとうございました。

    2015年6月17日 0:27