1

クラスを使用してすべての画面のガンマを増減し、プログラムを開始してガンマを増減すると正常に動作しますが、しばらくすると (20 秒程度) 動作しなくなり、問題があり、Graphics.FromHwnd(IntPtr.Zero).GetHdc().ToInt32();これを更新する必要があるようです。その後、再び機能します。サンプルコードでは、これは初期化中に一度だけ行われますが、機能させるためにSetBrightness()メソッド内に行を貼り付けたので、毎回更新されます。このようにしても大丈夫ですか、それとも問題が予想できますか?

これはコードです:

public static class Brightness
{
    [DllImport("gdi32.dll")]
    private unsafe static extern bool SetDeviceGammaRamp(Int32 hdc, void* ramp);

    private static bool initialized = false;
    private static Int32 hdc;

    private static void InitializeClass()
    {
        if (initialized)
            return;

        //Get the hardware device context of the screen, we can do
        //this by getting the graphics object of null (IntPtr.Zero)
        //then getting the HDC and converting that to an Int32.
        hdc = Graphics.FromHwnd(IntPtr.Zero).GetHdc().ToInt32();

        initialized = true;
    }

    public static unsafe bool SetBrightness(short brightness)
    {
        InitializeClass();

        hdc = Graphics.FromHwnd(IntPtr.Zero).GetHdc().ToInt32();

        if (brightness > 255)
            brightness = 255;

        if (brightness < 0)
            brightness = 0;

        short* gArray = stackalloc short[3 * 256];
        short* idx = gArray;

        for (int j = 0; j < 3; j++)
        {
            for (int i = 0; i < 256; i++)
            {
                int arrayVal = i * (brightness + 128);

                if (arrayVal > 65535)
                    arrayVal = 65535;

                *idx = (short)arrayVal;
                idx++;
            }
        }

        //For some reason, this always returns false?
        bool retVal = SetDeviceGammaRamp(hdc, gArray);

        //Memory allocated through stackalloc is automatically free'd
        //by the CLR.

        return retVal;

    }
}

これは、次のように呼ばれます。

short gammaValue = 128;

    void gammaUp_OnButtonDown(object sender, EventArgs e)
    {
        if (gammaValue < 255)
        {
            gammaValue += 10;
            if (gammaValue > 255)
                gammaValue = 255;
            Brightness.SetBrightness(gammaValue);
        }
    }

    void gammaDown_OnButtonDown(object sender, EventArgs e)
    {
        if (gammaValue > 0)
        {
            gammaValue -= 10;
            if (gammaValue < 0)
                gammaValue = 0;
            Brightness.SetBrightness(gammaValue);
        }
    }
4

4 に答える 4

1

これは、非常に単純で古典的なメモリ管理の問題です。

への参照を保持する必要があります

Graphics.FromHwnd(IntPtr.Zero)

そうしないと、ガベージ コレクター (ちなみに GC も) は、グラフィック コンテキスト (GC) を使用していないと判断します。

ref を維持するということは、「ライブ オブジェクト」と呼ばれることもある有効な参照を維持することを意味します。あなたのクラス Brightness ケースでは、(メモリから、コンパイルされていません!)に変換されます

private static bool initialized = false;
private static Graphics gc;

private static void InitializeClass()
{
    if (initialized)
        return;

    //Get the hardware device context of the screen, we can do
    //this by getting the graphics object of null (IntPtr.Zero)
    gc = Graphics.FromHwnd(IntPtr.Zero);

    initialized = true;
}

その後、 SetRamp(gc.GetHdc().ToInt32(), ...); を呼び出します。

重要な点は、Brightness が初期化されている限り、Graphics オブジェクトが必要であるため、ガベージ コレクターはそれを解放しないということです。

あなたはそれが数値ハンドル値であることを覚えているだけで、ReleaseDC() を明示的に呼び出すと、ランタイムの仕事をしていて、ランタイムが同じことをするとクラッシュしました。

于 2015-01-16T16:48:03.957 に答える
0

これは、によって返されたデバイス コンテキスト ハンドルGetHdc()を使用後に解放する必要があるためだと思います。できるだけ早くこれを行うと想定されています。SetDeviceGammaRamp()を介して呼び出した直後に解放できるはずですReleaseHdc()。基本的に、DC を取得するときはいつでも、できるだけ早くリリースする必要があります。

C# は、ここで Win32 GDI API に対して薄いベニアしか提供していないため、適用されるルールはGetDC()およびのルールであり、IntPtr.Zeroを使用して同じ効果 (および同じ問題) を pinvoke するReleaseDC()ことができます。GetDC()

GDIはこのようにうるさいです。ハンドルを「借用」しているだけで、取得したハンドルは通常、特定のサイズのプールから割り当てられます。それがなくなると、これ以上DCを取得することはできません.以前のものをリリースしました。ペイントする GDI ブラシまたはペンを繰り返し割り当てる場合も同様です。最終的には不足します。さらに、プールされたハンドルを保持することは、通常はうまくいきません。GDI内でこれに関する問題が何であるかを正確に伝えることはできませんが、以前に同様の問題に遭遇したことがあり、ドライバーの問題を除いて、DCを取得し、ランプを設定してから解放すると、アプローチが機能することが期待されますすぐにDC。

ちなみに、MSDN に記載されているように、これらのペアワイズ ルールには例外がありますCS_OWNDC。それらはプールから来ないので、好きなだけ保持できます。

ちなみに、x86 ビルドを強制しない限り、Int32 の代わりに IntPtr を使用することをお勧めします。SetDeviceGammaRamp()これは、常に 32 ビット値ではなくポインター サイズの整数であるためです。ではなく、構造体または固定長配列void*を定義します。しかし、これらのどちらも問題を引き起こしているとは思いません。その間、呼び出しに切り替えて pinvokeを介して、使用していない Graphics オブジェクトを切り取ることができます。PInvoke.netには (執筆時点で) と のより安全な定義があります。RAMPMarshalAsGetDC()ReleaseDC()SetDeviceGammaRamp()GetDC()ReleaseDC()

于 2015-01-13T22:41:52.023 に答える