Application.Run上的CallbackOnCollectedDelegate(new Form1())

时间:2011-05-26 22:22:33

标签: c# exception garbage-collection directx screen-capture

我制作了一个非常小的应用程序,使用SlimDX捕获游戏中的屏幕。 (我按左键单击捕捉)

捕获工作(至少当我点击表单本身时)但是当我点击firefox或任何其他应用程序时,我得到了这个例外:

  

对“CaptureScreen!CaptureScreen.Form1 + WinEventDelegate :: Invoke”类型的垃圾回收委托进行了回调。这可能会导致应用程序崩溃,损坏和数据丢失。将委托传递给非托管代码时,托管应用程序必须将它们保持活动状态,直到确保它们永远不会被调用为止。

在我的program.cs中的这一行:

  

Application.Run(new 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/

关于我如何解决这个问题的任何想法?

3 个答案:

答案 0 :(得分:12)

您的问题是您只是将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);

这应该确保您的代表在您需要时保持活着。

答案 1 :(得分:0)

我也有这个问题并且已经有类似的@dlev解决方案,但它不起作用。我发现如果你将成员标记为静态,它就会阻止它被收集。

private static WinEventDelegate winEventProc;

答案 2 :(得分:-1)

有一个MSDN链接可以帮助您解决问题 Let The CLR Find Bugs For You With Managed Debugging Assistants