Suppose I have a WeakReference of a target strong reference. I'd like to be informed when the target object itself is being collected by the GC. Is it possible?
EDIT: Adding code to the finalizer/destructor is not an option here. I need something that is not dependent on class code.
It's possible under .NET 4.0 and following using ConditionalWeakTable<TKey, TValue>. Thanks this, and other sites. It follows proof of concept code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;
namespace Test
{
public static class GCInterceptor
{
private static ConditionalWeakTable<object, CallbackRef> _table;
static GCInterceptor()
{
_table = new ConditionalWeakTable<object, CallbackRef>();
}
public static void RegisterGCEvent(this object obj, Action<int> action)
{
CallbackRef callbackRef;
bool found = _table.TryGetValue(obj, out callbackRef);
if (found)
{
callbackRef.Collected += action;
return;
}
int hashCode = RuntimeHelpers.GetHashCode(obj);
callbackRef = new CallbackRef(hashCode);
callbackRef.Collected += action;
_table.Add(obj, callbackRef);
}
public static void DeregisterGCEvent(this object obj, Action<int> action)
{
CallbackRef callbackRef;
bool found = _table.TryGetValue(obj, out callbackRef);
if (!found)
throw new Exception("No events registered");
callbackRef.Collected -= action;
}
private class CallbackRef
{
private int _hashCode;
public event Action<int> Collected;
public CallbackRef(int hashCode)
{
_hashCode = hashCode;
}
~CallbackRef()
{
Action<int> handle = Collected;
if (handle != null)
handle(_hashCode);
}
}
}
}
Tested with the following code:
public partial class Form1 : Form
{
private object _obj;
public Form1()
{
InitializeComponent();
_obj = new object();
_obj.RegisterGCEvent(delegate(int hashCode)
{
MessageBox.Show("Object with hash code " + hashCode + " recently collected");
});
}
private void button1_Click(object sender, EventArgs e)
{
_obj = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
What about Object.Finalize() method? Won't that be invoked upon finalization?
You may use interception to catch Finalize for each classes which is inherited from a custom interface/class. I think, this is what you want to try to achieve, right? You can use Unity for that. Here is a very short example how to do interception with Unity.
Related
I have the following two programs
(1) Windows forms application
(2) ClassLibrary1.dll
In the winform, the dll is loaded using appdomain. The winform subscribes to the action('TestAction') in the dll. I am trying to access the value returned by the dll action and use it in the event handler('HandleAction'). To do this I created a child class that inherits EventArgs. I changed the input arguments of the event handler(HandleAction) accordingly. But I am seeing a compiler error at Loader.Call( "RaiseAct", HandleAction, DateTime.Now.ToShortDateString()); . The error is:Cannot convert from method group to System.EventHandler
I have referred to other duplicates to this question, but they are not the same case. Any help will be very much appreciated. Thank you.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.Diagnostics;
using ClassLibrary1;
namespace WindowsFormsApplication2
{
[Serializable]
public class IntEventArgs : EventArgs
{
public int data;
}
[Serializable]
public partial class Form1 : Form
{
void HandleEvent(object sender, EventArgs e)
{
Debug.WriteLine("HandleEvent called");
}
void HandleAction(object sender, IntEventArgs e)
{
Debug.WriteLine("HandleAction called");
Debug.WriteLine("the value of index is " + e.data);
}
public Form1()
{
InitializeComponent();
Loader.Call( "RaiseEvent", HandleEvent, DateTime.Now.ToShortDateString());
Loader.Call( "RaiseAct", HandleAction, DateTime.Now.ToShortDateString()); // Cannot convert from method group to System.EventHandler
}
private void button1_Click(object sender, EventArgs e)
{
Application.Restart();
Application.Run(new Form1());
this.Close();
}
}
public class Loader : MarshalByRefObject
{
static string dll = #"..\ConsoleApplication1\ClassLibrary1\bin\Debug\ClassLibrary1.dll";
static AppDomain ad = AppDomain.CreateDomain("Test");
static Assembly a = Assembly.LoadFile(dll);
static object o = a.CreateInstance("ClassLibrary1.Class1");
static Type t = o.GetType();
object CallInternal1( string method, EventHandler handler, object[] parameters)
{
// Subscribe to the event
EventInfo eventInfo1 = t.GetEvent("TestEvent");
eventInfo1.AddEventHandler(o, handler);
MethodInfo m = t.GetMethod(method);
return m.Invoke(o, parameters);
}
object CallInternal2( string method, EventHandler handler, object[] parameters)
{
// Subscribe to the event
EventInfo eventInfo2 = t.GetEvent("TestAction");
eventInfo2.AddEventHandler(o, new Action<int>((index) => handler(null, new IntEventArgs { data = index })));
MethodInfo m = t.GetMethod(method);
return m.Invoke(o, parameters);
}
public static object Call( string method, EventHandler handler, params object[] parameters)
{
Loader ld = (Loader)ad.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(Loader).FullName);
object result = 0;
switch (method)
{
case "RaiseEvent":
{
result = ld.CallInternal1( method, handler, parameters);
break;
}
case "RaiseAct":
{
result = ld.CallInternal2( method, handler, parameters);
break;
}
}
return result;
}
}
}
This ClassLibrary1.dll code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ClassLibrary1
{
[Serializable]
public class Class1
{
public event EventHandler TestEvent;
public int RaiseEvent(string msg)
{
try
{
TestEvent(this, EventArgs.Empty);
}
catch (Exception ex)
{
Console.WriteLine("the exception is: " + ex.ToString());
if (ex.InnerException != null)
{
Console.WriteLine("the inner exception is: " + ex.InnerException.Message.ToString());
}
}
return 2;
}
public event Action<int> TestAction = Func;
public int RaiseAct(string msg)
{
TestAction(3);
return 5;
}
public static void Func(int a)
{
int g = 2;
}
}
}
I have a form that has a button to get a method executed in another class.
Code on the form:
public delegate void CustomPreviewCreate();
public static event CustomPreviewCreate CustomPreviewCreate_Do;
private void CreatePreview()
{
if (CustomPreviewCreate_Do !=null)
{
CustomPreviewCreate_Do();
}
}
This event then gets handled in another class. What I would like to achieve is that I can feed back to the form some form of return value if the method correctly executed.
What I tried so far does not get me the result.
Here is the code:
public void Initialize()
{
SubAsstViewPartControl.CustomPreviewCreate_Do += SubAsstViewPartControl_CustomPreviewCreate_Do;
// this gives me a the compiler error that the return type is wrong
}
private bool SubAsstViewPartControl_CustomPreviewCreate_Do()
{
// do stuff
return false;
}
Is there any direct way to return value from an event handler or I need to use a separate static field to store the event result in?
Update:
Per #Jon's comment, which seemed the simplest to me, I added an answer below demonstrating the simplest approach.
The common approach is to encapsulate your value in the type of EventArgs your event expects. For example, the Framework's CancelEventArgs contains a settable bool Cancel property, allowing each CancelEventHandler to assign a value. The sender can then read the property after the event has been invoked. You could also use a container-like EventArgs class if you want to collect separate values from individual event handlers. For example:
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
public class SingleValueEventArgs : EventArgs
{
public int Value { get; set; }
}
public class MultiValueEventArgs : EventArgs
{
private List<int> _values = new List<int>(); // Private to prevent handlers from messing with each others' values
public IEnumerable<int> Values
{
get { return _values; }
}
public void AddValue(int value) { _values.Add(value); }
}
public class Exposer
{
public event EventHandler<SingleValueEventArgs> WantSingleValue;
public event EventHandler<MultiValueEventArgs> WantMultipleValues;
public void Run()
{
if (WantSingleValue != null)
{
var args = new SingleValueEventArgs();
WantSingleValue(this, args);
Console.WriteLine("Last handler produced " + args.Value.ToString());
}
if (WantMultipleValues != null)
{
var args = new MultiValueEventArgs();
WantMultipleValues(this, args);
foreach (var value in args.Values)
{
Console.WriteLine("A handler produced " + value.ToString());
}
}
}
}
public class Handler
{
private int _value;
public Handler(Exposer exposer, int value)
{
_value = value;
exposer.WantSingleValue += exposer_WantSingleValue;
exposer.WantMultipleValues += exposer_WantMultipleValues;
}
void exposer_WantSingleValue(object sender, SingleValueEventArgs e)
{
Console.WriteLine("Handler assigning " + _value.ToString());
e.Value = _value;
}
void exposer_WantMultipleValues(object sender, MultiValueEventArgs e)
{
Console.WriteLine("Handler adding " + _value.ToString());
e.AddValue(_value);
}
}
class Program
{
static void Main(string[] args)
{
var exposer = new Exposer();
for (var i = 0; i < 5; i++)
{
new Handler(exposer, i);
}
exposer.Run();
}
}
}
Per Jon Skeet's comment, which seemed the simplest to me, the simplest approach seems to be as follows:
public delegate bool CustomPreviewCreate(); // here we declare a return type
public static event CustomPreviewCreate CustomPreviewCreate_Do;
private void CreatePreview()
{
if (CustomPreviewCreate_Do !=null)
{
bool returnval = CustomPreviewCreate_Do();
}
}
And then:
// the method is declared to return the same type
bool SubAsstViewPartControl_CustomPreviewCreate_Do()
{
// do stuff
return true; // return the value of the type declared
}
This problem has been addressed in SOF in general. However, I am unable (not competent enough) to apply the suggestions to this example. I am getting the "Cannot access a non-static member of outer type 'FixClientTest.Form1' via nested type ... " error. In this case, the nested type is an instantiation of a 3rd party provided class (in this case, the open-source QuickFix/n library). I understand that the source is not really relevant but I am trying to avoid any suggestion that might have me modifying that code and don't have the knowledge to get around the problem. My goal is simply to update form controls based on information that I get in the callbacks from this library. (The code below is just a simple form with 2 buttons, one to set up the event callbacks and the other to stop them.) I would greatly appreciate any suggestions that the community might have.
Thank You.
using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using QuickFix;
namespace FixClientTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public class MyQuickFixApp : QuickFix.IApplication
{
public void FromApp(QuickFix.Message msg, SessionID sessionID) { }
public void OnCreate(SessionID sessionID) { }
public void OnLogout(SessionID sessionID)
{
Console.WriteLine("Logged out.");
}
public void OnLogon(SessionID sessionID)
{
Console.WriteLine("Logged In.");
}
public void FromAdmin(QuickFix.Message msg, SessionID sessionID)
{
//logListView.Items.Add(msg.ToString()); <<GENERATES ERROR!
}
public void ToAdmin(QuickFix.Message msg, SessionID sessionID) { }
public void ToApp(QuickFix.Message msg, SessionID sessionID) { }
}
public QuickFix.Transport.SocketInitiator _Initiator = null;
private void connectButton_Click(object sender, EventArgs e)
{
string file = "c:/FIX/tradeclientIB.cfg";
try
{
QuickFix.SessionSettings settings = new QuickFix.SessionSettings(file);
QuickFix.IApplication myApp = new MyQuickFixApp();
QuickFix.IMessageStoreFactory storeFactory = new QuickFix.FileStoreFactory(settings);
QuickFix.ILogFactory logFactory = new QuickFix.ScreenLogFactory(settings);
_Initiator = new QuickFix.Transport.SocketInitiator(myApp, storeFactory, settings, logFactory);
_Initiator.Start();
}
catch (System.Exception err)
{
MessageBox.Show(err.ToString());
}
}
private void stopButton_Click(object sender, EventArgs e)
{
_Initiator.Stop();
}
}
}
Your MyQuickFixApp class doesn't know anything about the Form1 object.
First, I'd suggest moving it out of the Form1 class.
Then, I'd look at how and when the MyQuickFixApp object is created. I'm not familiar with the QuickFix library, but a quick glance at the docs suggests that creating it in a button message handler isn't the right way to do this. (In a winforms app I'd imagine you'd create it in your Program.Main method).
As for the actual error, you need to give the MyQuickFixApp object a reference to the Form1 object (probably pass it in the constructor).
public class MyQuickFixApp : QuickFix.IApplication
{
private readonly Form1 _form1;
public MyQuickFixApp(Form1 form)
{
_form1 = form;
}
public void FromAdmin(QuickFix.Message msg, SessionID sessionID)
{
_form1.logListView.Items.Add(msg.ToString());
}
}
While I was waiting for answers, I took another look at an answer provided in this link:
Property is inaccessible due to its protection level When I read it again more carefully this time, I realized that Steve had posted the answer I need so it is really his answer. I needed to restructure this using events to capture the change in the form and to preserve encapsulation. It also allowed me to move my class that updated the form out of the Form class, however, it created the much-feared "cross-thread" problem. I already had an answer for this from someone else (sorry, don't have that link). I added lines to the event that updates the form to spin it off as a new thread. I've done this before and it works nicely. If anyone thinks this might be a problem, please let me know. Apologies if I've wasted anyone's time in looking at my problem but I thought I would share the mix of answers because it solves two of the biggest problems I've had since learning c#/forms.
One thing, don't get confused about this declaration: private delegate void AddLogItemDelegate(string msg); It is related to creating the thread containing the AddLogItem function and not to the creation of the event.
Thanks
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using QuickFix;
namespace FixClientTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public QuickFix.Transport.SocketInitiator _Initiator = null;
private void connectButton_Click(object sender, EventArgs e)
{
string file = "c:/FIX/tradeclientIB.cfg";
try
{
QuickFix.SessionSettings settings = new QuickFix.SessionSettings(file);
QuickFix.IApplication myApp = new MyQuickFixApp();
QuickFix.IMessageStoreFactory storeFactory = new QuickFix.FileStoreFactory(settings);
QuickFix.ILogFactory logFactory = new QuickFix.ScreenLogFactory(settings);
//QuickFix.Transport.SocketInitiator initiator = new QuickFix.Transport.SocketInitiator(myApp, storeFactory, settings, logFactory);
_Initiator = new QuickFix.Transport.SocketInitiator(myApp, storeFactory, settings, logFactory);
MyQuickFixApp.UpdateEvent += new MyQuickFixApp.OnUpdateEvent(AddLogItem);
_Initiator.Start();
}
catch (System.Exception err)
{
Console.WriteLine(err.Message);
Console.WriteLine(err.StackTrace);
MessageBox.Show(err.ToString());
}
}
private void stopButton_Click(object sender, EventArgs e)
{
_Initiator.Stop();
}
public string stuff;
private delegate void AddLogItemDelegate(string msg);
public void AddLogItem(string msg)
{
if(this.InvokeRequired)
{
this.Invoke(new AddLogItemDelegate(AddLogItem), new object[] { msg });
return;
}
try
{
logListView.Items.Add(msg);
}
catch(Exception err)
{
MessageBox.Show(err.ToString());
}
}
}
public class MyQuickFixApp : QuickFix.IApplication
{
public delegate void OnUpdateEvent(string msg);
public static event OnUpdateEvent UpdateEvent;
public void FromApp(QuickFix.Message msg, SessionID sessionID) { }
public void OnCreate(SessionID sessionID) { }
public void OnLogout(SessionID sessionID)
{
Console.WriteLine("Logged out.");
}
public void OnLogon(SessionID sessionID)
{
UpdateEvent("STUFF!!");
Console.WriteLine("Logged In.");
}
public void FromAdmin(QuickFix.Message msg, SessionID sessionID)
{
}
public void ToAdmin(QuickFix.Message msg, SessionID sessionID) { }
public void ToApp(QuickFix.Message msg, SessionID sessionID) { }
}
}
Let's say I have an exposed interface as such:
interface IMyService
{
MyResult MyOperation();
}
This operation is synchronous and returns a value.
My implemented interface has to do the following:
Call an asynchronous method
Wait for event #1
Wait for event #2
This is due to a 3rd party COM object I am working with.
This code looks similar to the following
public MyResult MyOperation()
{
_myCOMObject.AsyncOperation();
//Here I need to wait for both events to fire before returning
}
private void MyEvent1()
{
//My Event 1 is fired in this handler
}
private void MyEvent2()
{
//My Event 2 is fired in this handler
}
My two events can happen in either order, it is quite random.
What is the proper threading mechanism I can use to synchronize this? I was using ManualResetEvent before I had to start waiting for the second event, and have not seen an easy way to use it for both events. These 2 events set variables that allow me to create the return value for MyOperation().
Any ideas on a good implementation for this? I have no control over the way the 3rd party object is implemented.
Two ManualResetEvents should do the trick for you. Just initialize them to false before you call the _myCOMObject.AsyncOperation(). Like this:
private ManualResetEvent event1;
private ManualResetEvent event2;
public MyResult MyOperation()
{
event1 = new ManualResetEvent(false);
event2 = new ManualResetEvent(false);
_myCOMObject.AsyncOperation();
WaitHandle.WaitAll(new WaitHandle[] { event1, event2 });
}
private void MyEvent1()
{
event1.Set();
}
private void MyEvent2()
{
event2.Set();
}
Edit
Thanks for the comments. I've changed the wait call to use WaitAll
My implementation example is as follows:
namespace ConsoleApplication1
{
class Program
{
private static WaitHandle[] waitHandles;
private static event EventHandler Evt1;
private static event EventHandler Evt2;
static void Main(string[] args)
{
waitHandles = new WaitHandle[]{
new ManualResetEvent(false),
new ManualResetEvent(false)
};
Evt1 += new EventHandler(Program_Evt1);
Evt2 += new EventHandler(Program_Evt2);
OnEvt1();
OnEvt2();
WaitHandle.WaitAll(waitHandles);
Console.WriteLine("Finished");
Console.ReadLine();
}
static void Program_Evt2(object sender, EventArgs e)
{
Thread.Sleep(2000);
((ManualResetEvent)waitHandles[0]).Set();
}
static void Program_Evt1(object sender, EventArgs e)
{
((ManualResetEvent)waitHandles[1]).Set();
}
static void OnEvt1()
{
if (Evt1 != null)
Evt1(null, EventArgs.Empty);
}
static void OnEvt2()
{
if (Evt2 != null)
Evt2(null, EventArgs.Empty);
}
}
}
I make it sleep for the purposes of this example and the WaitAll functionality
Cheers,
Andrew
P.S. another example would be using AsyncCallback, really quick and dirty example, but gives you more keys to open the door with :-) . Hope this helps!!
namespace ConsoleApplication1
{
class Program
{
private static WaitHandle[] waitHandles;
private static event EventHandler Evt1;
private static event EventHandler Evt2;
static void Main(string[] args)
{
waitHandles = new WaitHandle[]{
new ManualResetEvent(false),
new ManualResetEvent(false)
};
var callabck1 = new AsyncCallback(OnEvt1);
var callabck2 = new AsyncCallback(OnEvt2);
callabck1.Invoke(new ManualResetResult(null, (ManualResetEvent)waitHandles[0]));
callabck2.Invoke(new ManualResetResult(null, (ManualResetEvent)waitHandles[1]));
WaitHandle.WaitAll(waitHandles);
Console.WriteLine("Finished");
Console.ReadLine();
}
static void OnEvt1(IAsyncResult result)
{
Console.WriteLine("Setting1");
var handle = result.AsyncWaitHandle;
((ManualResetEvent)handle).Set();
}
static void OnEvt2(IAsyncResult result)
{
Thread.Sleep(2000);
Console.WriteLine("Setting2");
var handle = result.AsyncWaitHandle;
((ManualResetEvent)handle).Set();
}
}
public class ManualResetResult : IAsyncResult
{
private object _state;
private ManualResetEvent _handle;
public ManualResetResult(object state, ManualResetEvent handle)
{
_state = state;
_handle = handle;
}
#region IAsyncResult Members
public object AsyncState
{
get { return _state; }
}
public WaitHandle AsyncWaitHandle
{
get { return _handle; }
}
public bool CompletedSynchronously
{
get { throw new NotImplementedException(); }
}
public bool IsCompleted
{
get { throw new NotImplementedException(); }
}
#endregion
}
}
I am not sure I understood your question, but AutoResetEvent.WaitAll seems to solve your problem, if I got it right. It allows you to set more than one handler and it will only be released when all are set.
http://msdn.microsoft.com/en-us/library/z6w25xa6.aspx
ok, so I'm writing a duplicate logon "worker" class in C# and I've ran into a bit of a snag. My logic behind it, I thought, was flawless! :(
But, I can't for the life of me figure out why it's triggering on the first occurence rather than on just duplicates :(
namespace Lab.Core.BackgroundWorker {
using Lab.Core;
using Lab.Core.Net;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Windows.Forms;
public class MultiLogon : IWorker {
private static Hashtable LoggedOnUsers = new Hashtable();
private Thread _worker = null;
//private Thread m_UsersUpdate = null;
public delegate Boolean AddUserToCollectionDelegate(String user, String computer);
public delegate void ClearCollectionDelegate(String user);
public delegate Boolean IsUserLoggedInDelegate(String user);
public Boolean AddUserToCollection(String user, String computer) {
int retVal = MultiLogon.LoggedOnUsers.Count + 1;
if (String.IsNullOrEmpty(user) || String.IsNullOrEmpty(computer))
return false;
if (!MultiLogon.LoggedOnUsers.ContainsKey(user))
MultiLogon.LoggedOnUsers.Add(user, computer);
return (MultiLogon.LoggedOnUsers.Count == retVal);
}
public void ClearCollection() {
if (MultiLogon.LoggedOnUsers.Count > 0)
MultiLogon.LoggedOnUsers.Clear();
}
public Boolean IsUserLoggedIn(String user) {
if (String.IsNullOrEmpty(user))
return false;
return (LoggedOnUsers.Contains(user));
}
#region IWorker Members
public void Run(object obj) {
AddUserToCollectionDelegate add = new AddUserToCollectionDelegate(AddUserToCollection);
//ClearCollectionDelegate clear = new ClearCollectionDelegate(ClearCollection);
//IsUserLoggedInDelegate isLogged = new IsUserLoggedInDelegate(IsUserLoggedIn);
while (true) {
foreach (Computer c in ComputerList.Instance)
if (!add.Invoke(c.UserName, c.MachineName)) {
// duplicate! or not? :/
// Credit (through adoption of code) goes to:
// http://bytes.com/groups/net-c/263778-quickly-finding-duplicates-arraylist#post1059834
foreach (DictionaryEntry item in MultiLogon.LoggedOnUsers) {
MessageBox.Show((String)item.Key, (String)item.Value);
//NetworkMessage.Send((String)item.Value, String.Format("It is against lab policy to share your account with anyone other than yourself or use someone else's account! Logout immediately or further action will be taken. Your action has been logged."));
//OffenseManager.Instance.AddOffense((String)item.Key, null, String.Format("Account sharing - Computer: {0}", item.Value), false);
}
}
Thread.Sleep(750);
}
}
public void Start() {
_worker = new Thread(new ParameterizedThreadStart(Run));
_worker.IsBackground = true;
_worker.Start();
}
public void Stop() {
if (_worker.IsAlive)
_worker.Abort();
}
#endregion
}
}
Apologies for the long code file. I don't know exactly what to paste to help you guys help me. :/
Thanks in advance! :)
Could be a thread race condition.
Have you tried to lock the collection when you're searching/inserting into it?
I don't believe Hashtable are threadsafe.
You might want to put a
lock(this) {
}
block around anything accessing the hashtable.