/// <summary> /// Registers a UIHandler to handle the specified UI using a exsisiting Named Registration /// </summary> /// <param name="handler">UIHandler to be executed when the specified UI is shown</param> /// <param name="registrationName">name of the Registration entry in the NamedRegistrations.xml file compiled in the runtime dll</param> /// <param name="notification">The events that you want to handle the targed UI</param> public void RegisterUIHandler(UIHandler handler, string registrationName, UIHandlerNotification notification) { if (processMonitor != null) { throw new InvalidOperationException("You cannot register UIHandlers after the process is executed"); } if (!namedRegistrations.ContainsKey(registrationName)) { throw new ArgumentException("A registration with the name '" + registrationName + "' could not be found"); } UIHandlerRule namedHandler = namedRegistrations[registrationName]; //register the handler with the same information as the named entry handlerRules.Push(new UIHandlerRule(handler, namedHandler.ProcessName, namedHandler.WindowTitle, notification)); }
private void RegisterDefaultUIHandlers() { Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("NamedRegistrations.xml"); //HACK: VS.NET renames the file to include the full path but razzle does not (this is very annoying) if (stream == null) { stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Code.Microsoft.Test.Loaders.NamedRegistrations.xml"); } XmlDocument doc = new XmlDocument(); doc.Load(stream); stream.Close(); foreach (XmlElement element in doc.DocumentElement.SelectNodes("Registration")) { Type type = typeof(ApplicationMonitor).Assembly.GetType(element.GetAttribute("Handler"), true, false); UIHandler handler = (UIHandler)Activator.CreateInstance(type, true); handler.NamedRegistration = element.GetAttribute("Name"); if (element.GetAttribute("ProcessName").Length > 0) { handler.ProcessName = element.GetAttribute("ProcessName"); } if (element.GetAttribute("WindowTitle").Length > 0) { handler.WindowTitle = element.GetAttribute("WindowTitle"); } if (element.GetAttribute("AllowMultipleInvocations").Length > 0) { handler.AllowMultipleInvocations = bool.Parse(element.GetAttribute("AllowMultipleInvocations")); } UIHandlerRule rule = new UIHandlerRule(handler, handler.ProcessName, handler.WindowTitle, UIHandlerNotification.All); handlerRules.Push(rule); namedRegistrations[handler.NamedRegistration] = rule; } }
public UIHandlerRule(UIHandler handler, string processName, string title, UIHandlerNotification notification) { this.handler = handler; this.processName = processName; this.notification = handler.Notification; if (title != null) { //replace Managed resource macros with values //syntax: @@PARTIAL_ASSEMBLY_NAME;BASE_NAME;RESOURCE_ID@@ //You can usually identify these values by looking at the source code for the managed component you need resource strings from //The BASE_NAME is usually in a private static class called SR. You can also use ILDASM do dissassemble //the resource table and look up what you are interested in Regex regex = new Regex("@@(?<asmName>[^@;]*);(?<baseName>[^@;]*);(?<id>[^@;]*)@@", RegexOptions.IgnorePatternWhitespace | RegexOptions.ExplicitCapture); for (Match match = regex.Match(title); match.Success; match = match.NextMatch()) { //get the match groups string matchString = match.Groups[0].Value; string asmName = match.Groups["asmName"].Value; string baseName = match.Groups["baseName"].Value; string id = match.Groups["id"].Value; //load the assembly and get the resource string from the resource manager try { AssemblyName assemblyName = AssemblyName.GetAssemblyName(asmName); Assembly asm = AssemblyLoadContext.Default.LoadFromAssemblyName(assemblyName); ResourceManager resMan = new ResourceManager(baseName, asm); string value = resMan.GetString(id); //replace the macro with the resource value title = title.Replace(matchString, value); } catch (System.IO.FileNotFoundException) { // Do nothing. This is for the case of an unload-able assembly reference // This will happen when registering for both the 2.0 and 4.0 System.Windows.Forms resources for the ClickOnce install dialog, for instance. } catch (System.BadImageFormatException) { // Same as above, this just fails differently depending on the scenario. } } } if (title != null && title.ToLowerInvariant().StartsWith("unmanagedresourcestring:")) { string[] resourceLookup = title.Substring(24).Split(','); if (resourceLookup.Length != 2) { throw new ArgumentException("Resource identifiers must specify a dll and resouce id", "title"); } string filename = resourceLookup[0].Trim(); int resourceId = int.Parse(resourceLookup[1].Trim()); this.title = ResourceHelper.GetUnmanagedResourceString(filename, resourceId); } // Loads a static property from a given assembly from the work dir // Can be updated later to search different paths // Do not change the string handling to "To...Invariant()" as class name loading will fail. else if (title != null && title.ToLowerInvariant().StartsWith("property:")) { string[] inputs = title.Substring(9).Trim().Split(','); string property = inputs[0].Trim(); string assembly = inputs[1].Trim(); // LoadWithPartialName here should be OK since test execution tries to ensure // that only a matching TestRuntime.dll is present // and, currently the only AMC files using this feature reference solely TestRuntime. // We need to fix this if not. [Microsoft] #pragma warning disable 618 Assembly a = Assembly.LoadWithPartialName(assembly); #pragma warning restore 618 if (a == null) { throw new ArgumentException("Could not locate assembly " + assembly); } Type t = a.GetType(property.Substring(0, property.LastIndexOf(".")), true); PropertyInfo pi = t.GetProperty(property.Substring(property.LastIndexOf(".") + 1)); // Create this as a regular expression because for IE titles we can't predict "Windows" vs "Microsoft" Internet explorer this.title = "regexp:(" + pi.GetValue(null, null) + ")"; } else { this.title = title; } }