2

SlimDX を使用して、ゲーム内の画面をキャプチャする非常に小さなアプリケーションを作成しました。(左クリックを押してキャプチャします)

キャプチャは機能しますが (少なくともフォーム自体をクリックすると)、Firefox またはその他のアプリケーションをクリックするとすぐに、次の例外が発生します。

タイプ 'CaptureScreen!CaptureScreen.Form1+WinEventDelegate::Invoke' のガベージ コレクション デリゲートでコールバックが行われました。これにより、アプリケーションのクラッシュ、破損、データ損失が発生する可能性があります。デリゲートをアンマネージ コードに渡す場合、デリゲートが呼び出されないことが保証されるまで、マネージ アプリケーションによって保持される必要があります。

私のprogram.csのこの行で:

Application.Run(新しい Form1());

私の Form1.cs (デザイナー自体にはコントロールがありません)

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.IO;
    using System.Linq;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Windows.Forms;
    using Microsoft.DirectX.Direct3D;

    namespace CaptureScreen
    {
        public partial class Form1 : Form
        {
            private const uint WINEVENT_OUTOFCONTEXT = 0;
            private const uint EVENT_SYSTEM_FOREGROUND = 3;
            private const int WH_MOUSE_LL = 14;
            private const int WM_LBUTTONDOWN = 513;

            delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd,         int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);

            IntPtr m_hhook;

            [DllImport("user32.dll")]
            static extern bool UnhookWinEvent(IntPtr hWinEventHook);
            [DllImport("user32.dll")]
            static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr                 hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint         idThread, uint dwFlags);
            [DllImport("user32.dll")]
            static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

            public Form1()
            {
                m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND,         IntPtr.Zero, WinEventProc, 0, 0, WINEVENT_OUTOFCONTEXT);

                hookProc = new HookProc(LowLevelMouseProc);
                hook = SetWindowsHookEx(WH_MOUSE_LL, hookProc, GetModuleHandle(null), 0);

                InitializeComponent();
            }

            private void Form1_FormClosing(object sender, FormClosingEventArgs e)
            {
                UnhookWinEvent(m_hhook);
                UnhookWindowsHookEx(hook);
            }

            void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int         idChild, uint dwEventThread, uint dwmsEventTime)
            {
                if (eventType == EVENT_SYSTEM_FOREGROUND)
                {
                    StringBuilder sb = new StringBuilder(500);
                    GetWindowText(hwnd, sb, sb.Capacity);
                }
            }

            [DllImport("kernel32.dll")]
            static extern IntPtr GetModuleHandle(string moduleName);

            [DllImport("user32.dll")]
            static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint         dwThreadId);

            [DllImport("user32.dll")]
            public static extern int UnhookWindowsHookEx(IntPtr hhook);

            [DllImport("user32.dll")]
            static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, uint wParam, IntPtr lParam);
            delegate IntPtr HookProc(int nCode, uint wParam, IntPtr lParam);

            [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
            public static extern IntPtr GetForegroundWindow();

            private HookProc hookProc;
            private IntPtr hook;

            IntPtr LowLevelMouseProc(int nCode, uint wParam, IntPtr lParam)
            {
                if (nCode >= 0 && (IntPtr)wParam == (IntPtr)WM_LBUTTONDOWN)
                {
                    CaptureScreen();
                }
                return CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);
            }

            private void CaptureScreen()
            {
                StreamReader reader = new StreamReader(Path.GetFullPath("../../Counter.txt"));
                string currentpic = reader.ReadLine();
                if (string.IsNullOrEmpty(currentpic))
                    currentpic = "0";
                reader.Close();

                Bitmap bitmap = Direct3DCapture.CaptureWindow(GetForegroundWindow());
                bitmap.Save(Path.GetFullPath("../../ScreenCapture/Test" + currentpic + ".gif"),         ImageFormat.Gif);

                StreamWriter writer = new StreamWriter(Path.GetFullPath("../../Counter.txt"));
                writer.Write((int.Parse(currentpic)) + 1);
                writer.Close();
            }

            public readonly uint DWM_EC_DISABLECOMPOSITION = 0;
            public readonly uint DWM_EC_ENABLECOMPOSITION = 1;
            [DllImport("dwmapi.dll", EntryPoint = "DwmEnableComposition")]
            protected static extern uint Win32DwmEnableComposition(uint uCompositionAction);
        }
    }

画面をキャプチャするクラスは、 http ://spazzarama.wordpress.com/2009/02/07/screencapture-with-direct3d/ にあります。

これを修正する方法について何か考えはありますか?

4

3 に答える 3

15

あなたの問題は、WinEventProc を SetWinEventHook に渡すだけで、現在のメソッドが終了すると (すぐにではないとしても!) GCed の対象となるデリゲートを暗黙的に作成することです。その事実の結果が表示されます。

タイプ WinEventDelegate の Form1 の新しいメンバーを作成し、それをパラメーターとして使用する必要があります。

private WinEventDelegate winEventProc;

次に、SetWinEventHook への呼び出しでそれを利用します。

this.winEventProc = new WinEventDelegate(WinEventProc);
m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, this.winEventProc, 0, 0, WINEVENT_OUTOFCONTEXT);

これにより、必要な限りデリゲートが生き続けることが保証されます。

于 2011-05-26T22:35:23.373 に答える
1

私もこの問題を抱えており、@dlev と同様の解決策がすでに用意されていますが、機能しません。メンバーを静的にマークすると、収集されなくなります。

private static WinEventDelegate winEventProc;

于 2012-03-15T16:44:06.830 に答える
-1

問題の解決に役立つ MSDN リンクがあります。
マネージド デバッグ アシスタントを使用して CLR にバグを検出させる

于 2011-05-26T22:35:40.523 に答える