private object InvokeMethod(IxCoreColleague target, MethodInfo mi, object[] parameterList) { if (!ProcessMessages) return null; try { Trace.Indent(); if (invokeSwitch.TraceInfo) { if (parameterList.Length > 0 && parameterList[0] != null) Trace.WriteLine(" Invoking Method: " + mi.Name + "('" + parameterList[0].ToString() + "')" + " on " + target.ToString(), invokeSwitch.DisplayName); else Trace.WriteLine(" Invoking Method: " + mi.Name + " on " + target.ToString(), invokeSwitch.DisplayName); } #if false string objName = ""; objName = target.ToString() + target.GetHashCode().ToString(); // DLH TESTING - not finished yet... if (IsDisposedColleague(objName)) { Debug.WriteLine("##Not Invoking disposed object:"+objName); return null; } #endif /// ***************************************************************************** /// Have seen a stack situation that the Mediator has been disposed of after /// returning from this call - IOW's the following call allows re-entrance. /// That's why the exception follows to handle a known case when processing the /// ExitApplication msg. /// ***************************************************************************** object returnValue = mi.Invoke(target, parameterList); if (m_isDisposed) throw new DisposedInAnotherFrameException(); if (target == m_temporaryColleague && !mi.Name.StartsWith("OnDisplay")) { RemoveColleague(m_temporaryColleague); m_temporaryColleague = null; // only keep one temporary colleague at a time (menu based) } Trace.Unindent(); return returnValue; } //note that we don't want to catch just any kind of the exception here, //most exceptions will be invoked by the method that we actually called. //the only exceptions we want to catch are the ones that just mean that we failed to find //a suitable method. These we can report if in debug-mode, otherwise ignore. catch(System.ArgumentException error) { //I once spent close to an hour wondering what was causing the failure here. //The exception message was "Object type cannot be converted to target type." //the answer was that I had made the signature be a UIListDisplayProperties //when it should have been a UIItemDisplayProperties. The two can be pretty hard to //distinguish visually. (John Hatton) Debug.Fail("The method '"+mi.Name+"' was found but couldn't be invoked. Maybe has the wrong signature?", error.Message); } // catch(ConfigurationException error) // { // throw error; //already has good user notification message in it // } // catch(RuntimeConfigurationException error) // { // throw error; //already has good user notification message in it // } catch(TargetInvocationException error) { Exception inner = error.InnerException; //unwrap, for example, a ConfigurationException // See LT-1629 "Closing one db while opening another crashes". The following // two lines appear to fix this bug, although I'm not too happy about this // asynchronous behavior appearing where we (or at least I) don't expect it. // Unfortunately, that's inherent with message handling architecture when // handling one message allows other messages to be handled before it finishes. // - SteveMc if (inner is System.NullReferenceException && mi.Name == "OnChooseLangProject") { // We probably closed the target's window after choosing another project, // but before getting to this point in processing the ChooseLP message. So // ignore the exception. return null; } string s = "Something went wrong trying to invoke "+ target.ToString() +":"+ mi.Name +"()."; //if we just send on the error, then the caller //will find it more easy to trap particular kind of exceptions. On the other hand, //if the exception makes it all the way to the user, then we will really want to see this extra string (s) //at the top level. throw new Exception(s, inner); //if we were to just bring up the green box), then that makes it impossible for the caller to catch //the exception. In particular, the link-jumping column can fail if the object it is trying to jump to //has been deleted. This is really not an "error", and if we let the exception get back to the //jumping code, then it can notify the user more calmly. //SIL.Utils.ErrorReporter.ReportException(new ApplicationException(s, inner)); } return null; }
/// <summary> /// /// </summary> /// <param name="target"></param> /// <param name="methodName"></param> /// <param name="parameterTypes">Currently, use null here if you have a ref param</param> /// <param name="parameterList"></param> /// <returns>null or the MethodInfo if a matching one was found</returns> private MethodInfo CheckForMatchingMessage(IxCoreColleague target, string methodName, Type[] parameterTypes, object[] parameterList, Set<int> previous) { #if false //tostring here is too expensive to leave lying around Trace.Indent(); TraceVerboseLine(" Checking : "+ target.ToString()); #endif int x = target.GetHashCode(); if (previous.Contains(x)) { throw new ArgumentException("XCore Mediator encountered the same " + target.ToString() + " twice on check for " + methodName + ", as if there is a loop."); } #if false TraceVerboseLine("Adding "+target.ToString()+":"+x.ToString()); #endif previous.Add(x); //NB: I did not think about these flags; I just copied them from an example BindingFlags flags = BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Instance; Type type = target.GetType(); MethodInfo mi; // // By using the JetBrains dotTrace profiler, the addition of m_TypeMethodInfo is saving // a significant (>20%) amount of time by not having to make as many calls to the expensive // System.Type.GetMethod() // if (parameterTypes == null) // callers currently must use null here if they have a "ref" param (TODO) { string key = type.ToString() + "_" + methodName; if (m_TypeMethodInfo.ContainsKey(key)) { mi = m_TypeMethodInfo[key]; } else { mi = type.GetMethod(methodName, flags); m_TypeMethodInfo[key] = mi; } } else { string key2 = type.ToString() + "_" + methodName + "_" + parameterTypes.Length.ToString(); if (m_TypeMethodInfo.ContainsKey(key2)) { mi = m_TypeMethodInfo[key2]; } else { mi= type.GetMethod(methodName, flags, null, parameterTypes, null); m_TypeMethodInfo[key2] = mi; } } #if false Trace.Unindent(); #endif return mi; }