Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.73/11: Рейтинг темы: голосов - 11, средняя оценка - 4.73
210 / 132 / 8
Регистрация: 18.08.2010
Сообщений: 1,018
1

Создать ссылку на делегат, чтобы Garbage Collector его не уничтожал

09.04.2012, 21:58. Просмотров 1981. Ответов 8
Метки нет (Все метки)


Использую неуправляемый и управляемый код. И вот через некоторое время дебаггинга програма прерывается с ошибкой в студии:
CallbackOnCollectedDelegate was detected
Message: A callback was made on a garbage collected delegate of type 'ActiveWindowsHistory!ActiveWindowsHistory.Form1+WinEventDel egate::Invoke'. This may cause application crashes, corruption and data loss. When passing delegates to unmanaged code, they must be kept alive by the managed application until it is guaranteed that they will never be called.
Понимаю, что все дело в том, что нужно как-то сделать, чтобы в управляемом коде ссылка на этот делегат оставалась в продолжении всей жизни программы, а то этот делегат используется только в некправляемом коде, из-за чего сборшик мусора его уничтожает. Помогите так сделать, чтобы делегат не уничтожался сборщиком мусора. А то что-то никак не могу разобраться (в делегатах путаюсь, хоть и пристально читал в книжках о них, из-за этого проблемы с ними...)

Как этот WinEventDelegate присобачить...

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
 
namespace ActiveWindowsHistory
{
    public partial class Form1 : Form
    {
        delegate void WinEventDelegate(IntPtr hWinEventHook,
            uint eventType, IntPtr hwnd, int idObject,
            int idChild, uint dwEventThread, uint dwmsEventTime);
 
        const uint WINEVENT_OUTOFCONTEXT = 0;
        const uint EVENT_SYSTEM_FOREGROUND = 3;
 
        [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);
 
        ListBox m_list;
        IntPtr m_hhook;
 
        public Form1()
        {
            InitializeComponent();
            m_list = new ListBox();
            m_list.IntegralHeight = false;
            m_list.Dock = DockStyle.Fill;
            Controls.Add(m_list);
 
            Text = "SetWinEventHook Example";
 
            Load += new EventHandler(Program_Load);
            FormClosing +=
                new FormClosingEventHandler(Program_FormClosing);
        }
 
        void Program_Load(object sender, EventArgs e)
        {
            m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND,
                EVENT_SYSTEM_FOREGROUND, IntPtr.Zero,
                WinEventProc, 0, 0, WINEVENT_OUTOFCONTEXT);
        }
 
        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);
 
                m_list.Items.Insert(0,
                    "Switched to: " + sb.ToString());
            }
        }
 
        void Program_FormClosing(
            object sender, FormClosingEventArgs e)
        {
            UnhookWinEvent(m_hhook);
        }
    }
}
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
09.04.2012, 21:58
Ответы с готовыми решениями:

Как сделать, чтобы garbage collector быстрее проходил?
как сделать, чтобы garbage collector быстрее проходил? Если возможно на примере :)

Garbage collector
как можно просимулировать Garbage collector (написать программу в C#) ?

CLR и Garbage Collector
всем доброго времени суток! Если не вызывать GC.Collect() то среда CLR автоматически убирает...

Режимы Garbage Collector
Добрый день. Че-т не могу найти ответы на эти вопросы :) Какие есть режимы GC и как указать JVM...

8
Неадекват
1433 / 1187 / 229
Регистрация: 02.04.2010
Сообщений: 2,718
10.04.2012, 00:45 2
А объявить делегат в неуправляемом коде религия не позволяет? Все что находиться в блоке unsafe сборщик мусора не трогает.

Ну или можно GC.KeepAlive(объект на который нужна ссылка) юзать.
0
Эксперт .NET
14843 / 11230 / 2947
Регистрация: 17.09.2011
Сообщений: 18,813
10.04.2012, 01:05 3
Tolias28, создайте ссылку на делегат в теле класса или используйте структуру GCHandle - она специально для этого создана. Правда, ее тоже надо будет в теле класса создать для последующего освобождения.

Цитата Сообщение от freeba Посмотреть сообщение
Все что находиться в блоке unsafe сборщик мусора не трогает.
С чего это вдруг? Блок unsafe всего лишь позволяет выполняться некоторым инструкциям в неуправляемом контексте.

Цитата Сообщение от freeba Посмотреть сообщение
Ну или можно GC.KeepAlive(объект на который нужна ссылка) юзать.
GC.KeepAlive, к сожалению, работает только в контексте текущего метода. После завершения этого метода объект все равно может быть подчищен сборщиком.
0
Неадекват
1433 / 1187 / 229
Регистрация: 02.04.2010
Сообщений: 2,718
10.04.2012, 13:55 4
Мда, нехорошо получилось, действительно собирает и в небезопасном коде. В голове кавардак
0
210 / 132 / 8
Регистрация: 18.08.2010
Сообщений: 1,018
10.04.2012, 15:28  [ТС] 5
Цитата Сообщение от kolorotur Посмотреть сообщение
Tolias28, создайте ссылку на делегат в теле класса или используйте структуру GCHandle
Так дело в том, что я знаю, что нужно создать ссылку в теле класса! Я не знаю как именно ее создать И был бы очень благодарен увидеть код, как именно это делается, если можно...)
0
Эксперт .NET
14843 / 11230 / 2947
Регистрация: 17.09.2011
Сообщений: 18,813
10.04.2012, 15:31 6
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
 
namespace ActiveWindowsHistory
{
    public partial class Form1 : Form
    {
        delegate void WinEventDelegate(IntPtr hWinEventHook,
            uint eventType, IntPtr hwnd, int idObject,
            int idChild, uint dwEventThread, uint dwmsEventTime);
 
        GCHandle hnd;
        
        ...
 
        public Form1()
        {
            ...
 
            Load += new EventHandler(Program_Load);
            FormClosingEventHandler handler = new FormClosingEventHandler(Program_FormClosing);
            FormClosing += handler;
            hnd = GCHandle.Alloc(handler);
        }
 
        ...
 
        void Program_FormClosing(object sender, FormClosingEventArgs e)
        {
            UnhookWinEvent(m_hhook);
            hnd.Free();
        }
    }
}
1
210 / 132 / 8
Регистрация: 18.08.2010
Сообщений: 1,018
10.04.2012, 17:22  [ТС] 7
kolorotur, спасибо! Но... я внес указанные вами изменения и по прежнему через некоторое время появляется тоже самое сообщение CallbackOnCollectedDelegate was detected

Вот измененный код
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
 
namespace ActiveWindowsHistory
{
    public partial class Form1 : Form
    {
        delegate void WinEventDelegate(IntPtr hWinEventHook,
            uint eventType, IntPtr hwnd, int idObject,
            int idChild, uint dwEventThread, uint dwmsEventTime);
 
        GCHandle hnd;
 
        const uint WINEVENT_OUTOFCONTEXT = 0;
        const uint EVENT_SYSTEM_FOREGROUND = 3;
 
        [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);
 
        ListBox m_list;
        IntPtr m_hhook;
 
        public Form1()
        {
            InitializeComponent();
            m_list = new ListBox();
            m_list.IntegralHeight = false;
            m_list.Dock = DockStyle.Fill;
            Controls.Add(m_list);
 
            Text = "SetWinEventHook Example";
 
            Load += new EventHandler(Program_Load);
            FormClosingEventHandler handler = new FormClosingEventHandler(Program_FormClosing);
            FormClosing += handler;
            hnd = GCHandle.Alloc(handler);
        }
 
        void Program_Load(object sender, EventArgs e)
        {
            m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND,
                EVENT_SYSTEM_FOREGROUND, IntPtr.Zero,
                WinEventProc, 0, 0, WINEVENT_OUTOFCONTEXT);
        }
 
        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);
 
                m_list.Items.Insert(0, sb.ToString());
            }
        }
 
        void Program_FormClosing(
            object sender, FormClosingEventArgs e)
        {
            UnhookWinEvent(m_hhook);
            hnd.Free();
        }
    }
}
0
Эксперт .NET
14843 / 11230 / 2947
Регистрация: 17.09.2011
Сообщений: 18,813
10.04.2012, 17:29 8
Тьфу ты елки-палки, это я в примере накосячил - не тот делегат прицепил
Надо было так сделать:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
 
namespace ActiveWindowsHistory
{
    public partial class Form1 : Form
    {
        delegate void WinEventDelegate(IntPtr hWinEventHook,
            uint eventType, IntPtr hwnd, int idObject,
            int idChild, uint dwEventThread, uint dwmsEventTime);
 
        GCHandle hnd;
        ...
        void Program_Load(object sender, EventArgs e)
        {
            WinEventDelegate handler = new WinEventDelegate(WinEventProc);
            m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND,
                EVENT_SYSTEM_FOREGROUND, IntPtr.Zero,
                handler, 0, 0, WINEVENT_OUTOFCONTEXT);
            hnd = GCHandle.Alloc(handler);
        }
        ...
        void Program_FormClosing(
            object sender, FormClosingEventArgs e)
        {
            UnhookWinEvent(m_hhook);
            hnd.Free();
        }
    }
}
Приношу свои извинения.
1
210 / 132 / 8
Регистрация: 18.08.2010
Сообщений: 1,018
10.04.2012, 17:38  [ТС] 9
я даже понял, почему проблема не исчезла... Потому что проблемный объект, из-за которого возникает это сообщение, это не FormClosingEventHandler, для которого вы использовали GCHandle... Проблема возникает из-за уничтоженного делегата WinEventDelegate... Я пробовал делать вот так:
C#
1
hndWinEventDelegate = GCHandle.Alloc(WinEventDelegate);
но среда ругается я тип использую как переменную ((

Добавлено через 1 минуту
о, вы уже ответили Спасибо, сейчас попробую

Добавлено через 5 минут
kolorotur, огромное спасибо за помощь! Все работает!)
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
10.04.2012, 17:38

Заказываю контрольные, курсовые, дипломные и любые другие студенческие работы здесь или здесь.

Посмотреть какой Garbage Collector выбран по умолчанию
Как узнать какой Garbage Collector выбран по умолчанию ? В командную строку пишу: java...

когда есть смысл вручную вызывать Garbage collector
когда есть смысл вручную вызывать Garbage collector?

Кто может кратко объясните как работает Garbage Collector?
кто может кратко объясните как работает Garbage Collector?

Делегат, как вызвать ссылку на функцию в Мейне
Пробую работу с делегатами. Немного не пойму, как с ними работать. Если пишу все в одном...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
9
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.