i'm tryng to have an handle the event every time I click on a chart, on each worksheet of an opened workbook.
I'm using the SheetActivate event like this:
private static void Xlapp_SheetActivate(Object obj_Ws)
{
myCharts.SetAllCharts((XL.Worksheet)obj_Ws);
}
to call a method every time a worksheet is activated, and then the "SetAllCharts" method in the "MyCharts" class that looks like:
internal void SetAllCharts(object obj_Ws)
{
XL.Worksheet Ws = (XL.Worksheet)obj_Ws;
XL.ChartObjects ChObj = Ws.ChartObjects();
if (ChObj.Count > 0)
{
xlapp.StatusBar = ("Setting " + ChObj.Count + " charts");
foreach (XL.ChartObject obj_Chart in ChObj)
{
XL.Chart myChart = obj_Chart.Chart;
myChart.MouseDown += myChart_MouseDown;
}
}
}
for now, the method handler (simply a xlapp.StatusBar message that reports X and Y of the click) works... only once, the first time I activate a worksheet, on the first click on a chart. After that i'm not getting the message box anymore...
What am I doing wrong??
You have to be careful with the lifetime of the COM objects (or at least the magic .NET wrappers around them) that you set event handlers on. They might be created on the fly as you enumerate a collection and garbage collected soon after, losing your event handler in the process. That's why it works once or a few times.
The test for this, before you start thinking about how to write the code, is to keep the objects in some higher object level or global list.
In your code this would be the obj_Chart object you are enumerating. Just make sure these objects are all kept alive.
(I find this stuff a bit confusing because the opposite happen with WPF - I think the event handlers prevent things from garbage collecting?)
Related
I am using Unity 3D, however, that information should be irrelevant for solving this problem as the core problem is System.Delegate (I wanted to let you know as I'll be linking to some Unity docs for clarification).
I have a custom window that has a custom update function DirectorUpdate. I need this function to run every editor update regardless of what the user/window is doing.
In order for this to be called every editor update, I Combine my method with the Delegate EditorApplication.update:
protected void OnEnable()
{
// If I do the below, base EditorApplication.update won't be called anymore.
// EditorApplication.update = this.DirectorUpdate;
// So I need to do this:
EditorApplication.update = (EditorApplication.CallbackFunction)System.Delegate.Combine(new EditorApplication.CallbackFunction(this.DirectorUpdate), EditorApplication.update);
... // Other stuff
}
Note that this is done inside a window's OnEnable.
The problem is that OnEnable can be called more than once during a single run (for example, when closing the window and then reopening the window during a single editor session) causing
EditorApplication.update = (EditorApplication.CallbackFunction)System.Delegate.Combine(new EditorApplication.CallbackFunction(this.DirectorUpdate), EditorApplication.update);
to be called multiple times, meaning my update method (this.DirectorUpdate) will eventually get called multiple times per update, which is causing some serious bugs.
So, the question is how do I check if EditorApplication.update already has my method "inside" of it. (By inside of it, I of course mean it has already been System.Delegate.Combine(d) to the delegate.)
I am aware that there could be other solutions, for example restoring EditorApplication.update to what it was prior when the window closes, however that won't account for all situations (for example, OnEnable also gets called during window refresh and such) and the bug will persist. (Also, what if another window Concatenates with EditorApplication.update while this window is open?) As such, the best solution would be to check if EditorApplication.update is already callin this method BEFORE Delegate.Combine-ing it.
I think you took the complicated road ;)
Subscribing and unsubscribing events and delegates is as simple as using the operators += and -= like
protected void OnEnable()
{
// You can substract your callback even though it wasn't added so far
// This makes sure it is definitely only added once (for this instance)
EditorApplication.update -= DirectorUpdate;
// This basically internally does such a Combine for you
EditorApplication.update += DirectorUpdate;
... // Other stuff
}
private void OnDisable()
{
// Remove the callback once not needed anymore
EditorApplication.update -= DirectorUpdate;
}
This way you can also have multiple instances of this window open and they all will receive the callback individually.
Btw if this is actually about an EditorWindow then afaik you should not use OnEnabled but you would rather use Awake
Called as the new window is opened.
and OnDestroy.
I am not familiar with what System.Delegate.Combine(d) does, but you can consider instead of enabling/disabling your window, destroying and instantiating it every time, and move your code to the Start or the Awake for it to be called only once per window "activation".
Last but not least, use a mighty boolean in the OnDisable so that you can handle the combine execution if your component was ever disabled. Like so:
bool omgIWasDisabled;
protected void OnEnable()
{
if (!omgIWasDisabled) {
EditorApplication.update = (EditorApplication.CallbackFunction)System.Delegate.Combine(new EditorApplication.CallbackFunction(this.DirectorUpdate), EditorApplication.update);
}
... // Other stuff
}
void OnDisable() {
omgIWasDisabled = true;
}
Hope any of those works out.
I'm writing a VSTO for Outlook 2010 (though it needs to work on 2010-2016, and for the most part does). I'm running into a bizarre problem with, as far as I can tell, event handlers never being removed. This means I can't avoid repeatedly invoking event handlers, which is silly and wasteful.
The code in question happens in the event handler for an Explorer's SelectionChange event. The handler checks whether the selection is a MailItem, and if so, it ensures that the Reply, ReplyAll, and Forward events have a handler. Since a given item may be selected more than once, the SelectionChange handler first removes the Reply/ReplyAll/Forward event handlers, in keeping with the pattern shown here (Prevent event handler being hooked twice, when you don't control the event-bearing class implementation).
The problem is, this isn't preventing the Reply (or other response action) event handler from being called once per instance of the SelectionChange event handler firing. This rapidly reaches a silly number of invocations. I thought it might be a synchronization issue, so I wrapped the event handler removal-and-adding in a lock block, to no avail.
private void SelectionChangeHandler()
{
Outlook.Selection sel = Application.ActiveExplorer().Selection;
// First make sure it's a (single) mail item
if (1 != sel.Count)
{ // Ignore multi-select
return;
}
// Indexed from 1, not 0. Stupid VB-ish thing...
Outlook.MailItem mail = sel[1] as Outlook.MailItem;
if (null != mail)
{
Outlook.ItemEvents_10_Event mailE = mail as Outlook.ItemEvents_10_Event;
lock (this)
{ // For each event, remove the handler then add it again
mailE.Forward -= MailItemResponseHandler;
mailE.Forward += MailItemResponseHandler;
mailE.Reply -= MailItemResponseHandler;
mailE.Reply += MailItemResponseHandler;
mailE.ReplyAll -= MailItemResponseHandler;
mailE.ReplyAll += MailItemResponseHandler;
}
ProcessMailitem(mail);
}
}
And the event handler that is being called way too many times:
private void MailItemResponseHandler (object newItem, ref bool Cancel)
{ // We need to get the responded-to item
// NOTE: There really needs to be a better way to do this
Outlook.MailItem old = GetCurrentMail();
if (null == old)
{ // No mail item selected
return;
}
MessageBox.Show(old.Body);
}
This function will eventually do something much more useful than pop a dialog box, but that was a convenient check for "did I find the correct original message?". I shouldn't be getting the same dialog box over and over again, though, and I am.
Am I doing something wrong? Is this a bug in Outlook, or in VSTO? Anybody know how I can avoid getting the duplicate event handler invocations?
Firstly, mailE variable must be declared on the class level, not local, to prevent it from being garbage collected.
Secondly, before setting the event, the old value (mailE) must be released using Marshal.ReleaseComObject.
The event handler is not actually being attached to the MAPI item that Outlook works with. Instead, it's being attached to a .NET object, called a Runtime Callable Wrapper (RCW), which wraps a COM object. Because of the way RCWs work, getting more than one reference to what seems to be the same object - for example, by getting the activeExplorer.Selection()[1] twice - gives multiple RCWs around different COM objects. This meant that the Outlook.MailItem (or Outlook.ItemEvents_10_Event) that I was attempting to remove events from didn't actually have any events on it; it was new-made each time SelectionChangeHandler fired.
Relatedly, because the only reference to an RCW-wrapped COM object is the RCW itself, letting all variables referencing an RCW go out of scope (or otherwise cease to reference that RCW) will result in the RCW being garbage collected (during which the COM object will be released and deleted). This has two relevant impacts on the code in question:
Because the garbage collection is not immediate, old RCWs (and COM objects) with existing event handlers were lingering on, and Outlook could still trigger events on their COM objects. This is why the event handler would be invoked multiple times.
Because the RCW was going out of scope at the bottom of SelectionChangeHandler, it was only a matter of time until garbage collection swept up all of the RCWs (and event handlers) and released all their COM objects. At that point, no events would be attached to that email.
While in practice, my testing happened over a short enough timeframe that I was more likely to get multiple live RCWs than none, selecting a mail item and not interacting with it (or selecting anything else) for long enough to trigger a garbage collection sweep would, in fact, result in the MailItemResponseHandler not being invoked at all when I clicked Reply.
#DmitryStreblechenko gave me the push in the right direction to work this out, but it took some experimentation to figure out. First of all, the relevant MailItem needed to be globally referenced, so its reference to the evented RCW wouldn't go out of scope and, also importantly, so its RCW could still be directly referenced when the SelectionChangeHandler was invoked again. I renamed the variable selectedMail and referenced it at the class level like such:
Outlook.ItemEvents_10_Event selectedMail;
I then modified SelectionChangeHandler so that whenever it is invoked with a single MailItem currently selected, it first removes all the event handlers from selectedMail, and only then points selectedMail to the newly-selected item. The RCW previously reference by selectedMail becomes eligible for garbage collection, but it has no event handlers on it so we don't really care. SelectionChangeHandler then adds the relevant event handlers to the new RCW now referenced by selectedMail.
private void SelectionChangeHandler()
{
Outlook.Selection sel = activeExplorer.Selection;
// First make sure it's a (single) mail item
if (1 != sel.Count)
{ // Ignore multi-select
return;
}
// Indexed from 1, not 0. Stupid VB-ish thing...
Outlook.MailItem mail = sel[1] as Outlook.MailItem;
if (null != mail)
{
if (null != selectedMail)
{ // Remove the old event handlers, if they were set, so there's no repeated events
selectedMail.Forward -= MailItemResponseHandler;
selectedMail.Reply -= MailItemResponseHandler;
selectedMail.ReplyAll -= MailItemResponseHandler;
}
selectedMail = mail as Outlook.ItemEvents_10_Event;
selectedMail.Forward += MailItemResponseHandler;
selectedMail.Reply += MailItemResponseHandler;
selectedMail.ReplyAll += MailItemResponseHandler;
if (DecryptOnSelect)
{ // We've got a live mail item selected. Process it
ProcessMailitem(mail);
}
}
}
Based on Dmitri's answer and comments, I tried calling Marshal.ReleaseComObject(selectedMail) on the old value of selectedMail before I thought to remove the event handlers instead. This helped a little, but either the COM objects are not released immediately or Outlook can still invoke event handlers through them, because events were still firing multiple times if I selected a given email multiple times in a short period before hitting Reply.
There's still one glitch to work out. If I open an inspector and hit Reply in there without changing my selection in the Explorer, it works fine (the MailItemResponseHandler is invoked). However, if I leave the inspector open, switch back to the explorer and select a different email, and then return to the inspector and hit Reply, it doesn't work. If there's an inspector open for the relevant email when that email gets de-selected, I need to avoid removing the event handlers (and then I do need to remove them when the inspector gets closed, unless the email is still selected in the explorer). Messy, but I'll work it out.
Edit: For clarification, this is in Unity, and is tied to Update(). So the events will be triggered once per frame for any number of frames. Both events can be triggered independently by the user, but there is an overlap in which both can be triggered. In this case I only want one of the two methods that will be called by the two events to actually run.
I'm trying to make a method that is called by one event and other method that is called by a 2nd event be linked in a way that only one of those two methods can be called or ran at the same time.
The events are triggered by an action of the user, both events can be triggered at the same time. But I only want one of the two methods that would be called to actually run if both events are triggered.
I was thinking something along the like of the following example, obviously the example may not be accurate, but the gist of what I am trying to accomplish is there.
Pseudo Code Example:
public delegate void OneHandler();
public event OneHandler OneEvent;
private void One()
{
if(some requirement && TEvent != null)
{
TEvent();
}
}
public delegate void TwoHandler();
public event TwoHandler TwoEvent;
private void Two()
{
if(some requirement && TEvent != null)
{
TEvent();
}
}
SomeClass.OneEvent += ActionOne();
SomeClass.TwoEvent += ActionTwo();
private void ActionOne()
{
if(TwoEvent is not firing)
{
Do Something;
}
}
private void ActionTwo()
{
if(OneEvent is not firing)
{
Do Something;
}
}
A crude diagram. The tan area moves down/left, the green areas move diagonally. I essentially want the tan area which have their own events, to not run their methods if the mouse is within that entire area.
Events fire one at a time, not all at once. Even if the same user action was going to cause both events to fire one after the other, there is no way to know that both events will fire, or in what order they will fire.
To be more clear: I didn't mean events can never be fired concurrently. I mean that, in general, there is no piece of code that determines, for a particular situation, the complete set of events which will be fired. You can't tell ahead of time which will fire, or in which order, or even on which threads.
For example, a particular user interface action (or user gesture) might cause several events to fire. In general, you can't depend on the number of events to be fired, or the order in which they will be fired.
There are some situations, like in ASP.NET web forms, where you can be assured that if the user clicks a button which has a Click event handler, that the page Load event will fire and then that the button Click event will fire. But this is because this behavior is defined and documented that way. If the order changed, or if, for instance, the page Load event stopped firing, then this would break a large number of ASP.NET web forms applications.
If all you're trying to do is guarantee that concurrent triggering (like two threads running on different cores) will never be allowed, you'll want to use something like a mutex. You would have both handlers perform a WaitOne(0) to see if the other handler is working, returning immediately if the call returns false (since the other method must be executing) then in a guaranteed-execution block (such as a finally) you would release the mutex.
How about use bool variable as flag ?
private bool flag[2] = [false, false];
private void ActionOne()
{
flag[0] = true;
if(!(flag[0])&&flag[1]))
{
Do Something;
}
flag[0] = false;
}
private void ActionTwo()
{
flag[1] = true;
if(!(flag[0] && flag[1]))
{
Do Something;
}
flag[1] = false;
}
I'm working on a Windows Service that where I am attempting to use Parallel.ForEach to spawn unique timed threads. The problem is that if I leave the code alone for several hours in VS or if I stop the service for a few hours and start anything back up - the initial start up code executes twice. Here is a snippet from the static void that the service's OnStart is calling.
Parallel.ForEach(urls, url =>
{
PageGrabber pagegrab = new PageGrabber(url);
if (url.Type.ToLower() == "http")
{
pagegrab.Elapsed += (obj, e) =>
{
pagegrab.CheckNormal();
};
pagegrab.CheckNormal();
}
else
{
pagegrab.Elapsed += (obj, e) =>
{
pagegrab.CheckXML();
};
pagegrab.CheckXML();
}
}
);
This works great if I use Threads directly, but really wanted to update this code a bit. The duplicate execution happens immediately. The PageGrabber object is pretty simple in that it simply uses a WebClient to download either HTML or XML as a string - pretty boring.
I think the problem is that you've subscribed to the Elapsed event by pageGrabber.Elapsed +=...
It is possible for that event to be raised or not.
So in some conditions if the event raised, your method will be called twice, otherwise it will be called once.
I don't think that you could resolve this problem by changing the parallel implementation (using task array instead of Parallel.Foreach). It just might cause the problem occur less often, which is a very bad symptom in parallel programming. You shouldn't let the problems to fade out by making their preconditions of happening harder! You should totally remove them!
So mehrandvd was on the right path. When creating an instance of my class, that used a System.Timers.Timer, it was firing the Elapsed event immediately because the Interval property wasn't being set correctly. Thus:
pagegrab.Elapsed += (obj, e) =>
{
pagegrab.CheckXML();
};
pagegrab.CheckXML();
Caused duplicate execution when nothing had happened in a while because the instance of the class that had the Interval set correctly was no longer in memory. My stupidity - all fixed now. Thanks for all the comments and suggestions.
I am maintaining some code which has two FileSystemWatcher events that makes it difficult to debug (and it has an error). So my idea is to simplify the code by making the execution sequential. Pretty much like this:
Main method
1) normal code here
2) enable event 1, let it check for files, disable it when it is done running once
3) enable event 2, let it check for files, disable it when it is done running once
Then the database logs would make more sense. I would be able to see which part of the program that is doing something wrong.
private void InitializeFileSystemWatcher()
{
this.MessageMonitor = new FileSystemWatcher(this.MessagePath, this.MessageFilter);
this.MessageMonitor.IncludeSubdirectories = true; // Recursive.
this.MessageMonitor.Created += new FileSystemEventHandler(OnMessageReceived);
this.MessageMonitor.EnableRaisingEvents = true;
}
From the main, I can set the EnableRaisingEvents=true to EnableRaisingEvents=false. Both events indexes the files in some folder and enacts a callback method.
My question is this: If the event is currently executing and I set EnableRaisingEvents=false, will it pause or continue to execute until it finishes?
If it does continue, I figure just to have a bool doRUN variable set at beginning and the end of the event as a check for the main method.
You should just detach the event handler after you check to make sure that it is working properly and then instantiate the second FileSystemWatcher.
Inside of the OnMessageReceived you could od something like
public void OnMessageRecieved(Object sender, Events e) //Not the real signature
{
MessageMonitor.Created -= OnMessageReceived();
//Do Your things
OtherMessageMonitor.Created += OnMessageReceived();
}