CallbackOnCollectedDelegate в Application.Run (новый Form1 ()) - PullRequest
2 голосов
/ 27 мая 2011

Я сделал очень маленькое приложение, которое захватывает экран внутри игр, используя SlimDX.(Я нажимаю левой кнопкой мыши, чтобы захватить)

Захват работает (по крайней мере, когда я нажимаю на саму форму), но как только я нажимаю на Firefox или любое другое приложение, я получаю это исключение:

Был выполнен обратный вызов делегата со сборщиком мусора типа 'CaptureScreen! CaptureScreen.Form1 + WinEventDelegate :: Invoke'.Это может вызвать сбои приложения, повреждение и потерю данных.При передаче делегатов в неуправляемый код управляемое приложение должно поддерживать их работу до тех пор, пока не будет гарантировано, что они никогда не будут вызваны.

в этой строке в моей программе .cs:

Application.Run (новый Form1 ());

My 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/

Есть идеи, как мне это исправить?

Ответы [ 3 ]

13 голосов
/ 27 мая 2011

Ваша проблема в том, что вы просто передаете WinEventProc в SetWinEventHook, который неявно создаст делегата, который может быть GCed, после выхода из текущего метода (если не раньше!). Вы видите последствия этого факта.

Вам потребуется создать новый член Form1 типа WinEventDelegate и использовать его в качестве параметра:

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);

Это должно гарантировать, что ваш делегат останется в живых столько, сколько вам нужно.

0 голосов
/ 15 марта 2012

У меня тоже была эта проблема, и у меня уже есть аналогичное решение @dlev, но оно не работает.Я обнаружил, что если вы помечаете элемент как статический, он предотвращает его сбор.

private static WinEventDelegate winEventProc;

0 голосов
/ 27 мая 2011

Существует ссылка MSDN, которая может помочь вам решить вашу проблему. Пусть CLR найдет для вас ошибки с помощью помощников по отладке

...