Я пытаюсь создать Конкурс популярности для форм в нашем основном интерфейсе. Есть много предметов, которые больше не используются, но получить подробную информацию о том, какие из них больше не используются, оказывается затруднительным.
Поэтому мне пришла в голову идея регистрировать форму, когда она загружается, и через год или около того я буду руководить группой и узнаю, какие формы используются, как часто и кем. Теперь проблема в том, что я не хочу добавлять строку в каждый блок InitializeComponent форм. Вместо этого я хотел бы поместить это в файл Program.cs и кое-как перехватить все загрузки форм, чтобы я мог их записать.
Это возможно?
Редактировать
Используя комментарий @ Jimi, я смог придумать следующее.
using CrashReporterDotNET;
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Windows.Automation;
using System.Windows.Forms;
namespace Linnabary
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
//This keeps the user from opening multiple copies of the program
string[] clArgs = Environment.GetCommandLineArgs();
if (PriorProcess() != null && clArgs.Count() == 1)
{
MessageBox.Show("Another instance of the WOTC-FE application is already running.");
return;
}
//Error Reporting Engine Setup
Application.ThreadException += ApplicationThreadException;
AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
//This is the SyncFusion License Key.
Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("<Removed>");
//Popularity Contest
Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent,
AutomationElement.RootElement, TreeScope.Subtree, (UIElm, evt) =>
{
try
{
AutomationElement element = UIElm as AutomationElement;
string AppText = element.Current.Name;
if (element.Current.ProcessId == Process.GetCurrentProcess().Id)
{
Classes.Common.PopularityContest(AppText);
}
}
catch (Exception)
{
//throw;
}
});
Application.Run(new Forms.frmMain());
}
private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs unhandledExceptionEventArgs)
{
ReportCrash((Exception)unhandledExceptionEventArgs.ExceptionObject);
Environment.Exit(0);
}
private static void ApplicationThreadException(object sender, ThreadExceptionEventArgs e)
{
ReportCrash(e.Exception);
}
public static void ReportCrash(Exception exception, string developerMessage = "")
{
var reportCrash = new ReportCrash("<Removed>")
{
CaptureScreen = true,
DeveloperMessage = Environment.UserName,
ToEmail = "<Removed>"
};
reportCrash.Send(exception);
}
public static Process PriorProcess()
{
Process curr = Process.GetCurrentProcess();
Process[] procs = Process.GetProcessesByName(curr.ProcessName);
foreach (Process p in procs)
{
if ((p.Id != curr.Id) && (p.MainModule.FileName == curr.MainModule.FileName))
{
return p;
}
}
return null;
}
}
}
Тем не менее, мне интересно, есть ли способ получить имя формы вместо ее текста. Так как он обращается ко ВСЕМ окнам и поэтому находится за пределами управляемого пространства, я сомневаюсь в этом. Тем не менее, это работает, и я опубликую это как ответ завтра, если никто не сделает этого.
3 ответа
Я публикую код, необходимый для обнаружения и регистрации активности форм, для тестирования или сравнения.
Как показано, этот код необходимо вставить только в файл Program.cs
внутри метода Main .
Эта процедура регистрирует каждый новый открытый заголовок / заголовок формы и имя формы.
Другие элементы могут быть добавлены в журнал, возможно, с помощью специального метода.
Когда новый WindowPattern.WindowOpenedEvent событие обнаруживает, что создано новое окно, AutomationElement.ProcessId
сравнивается с ProcessId приложения, чтобы определить, принадлежит ли новое окно к приложению.
Затем Application.OpenForms()
коллекция анализируется с использованием Form.AccessibleObject приведен к Control.ControlAccessibleObject для сравнения AutomationElelement.NativeWindowHandle
со свойством Form.Handle
, чтобы избежать вызова потока пользовательского интерфейса для получения дескриптора формы (который может генерировать исключения или блокировки потока, поскольку формы только загружаются в то время).
using System.Diagnostics;
using System.IO;
using System.Security.Permissions;
using System.Windows.Automation;
static class Program
{
[STAThread]
[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlAppDomain)]
static void Main(string[] args)
{
Automation.AddAutomationEventHandler(
WindowPattern.WindowOpenedEvent, AutomationElement.RootElement,
TreeScope.Subtree, (uiElm, evt) => {
AutomationElement element = uiElm as AutomationElement;
if (element == null) return;
try
{
if (element.Current.ProcessId == Process.GetCurrentProcess().Id)
{
IntPtr elmHandle = (IntPtr)element.Current.NativeWindowHandle;
Control form = Application.OpenForms.OfType<Control>()
.FirstOrDefault(f => (f.AccessibilityObject as Control.ControlAccessibleObject).Handle == elmHandle);
string log = $"Name: {form?.Name ?? element.Current.AutomationId} " +
$"Form title: {element.Current.Name}{Environment.NewLine}";
File.AppendAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "formLogger.txt"), log);
}
}
catch (ElementNotAvailableException) { /* May happen when Debugging => ignore or log */ }
});
}
}
Да, это должно быть легко. Существуют перехватчики событий, такие как OnLoad, OnShow, OnClose () для всех форм и большинства пользовательских элементов управления. Если вы хотите увидеть, на более детальном уровне, какие элементы управления используются вашими пользователями, вы можете подключить OnClick (), OnMouseOver () и около сотни других событий.
... и вы можете создавать свои собственные события.
Итак, подключите события, выбрав форму, а затем свойства (щелкните правой кнопкой мыши или клавишу F4). В верхнем окне свойств у вас есть кнопка «Показать события», которая выглядит как молния. Нажмите на это, а затем выберите из списка событие, которое вы хотите использовать для этой регистрации.
Применение IMessageFilter к приложению для обнаружения WM_Create, а затем определите, является ли целевой дескриптор принадлежал бы Form
был бы идеальным решением с минимальным ударом по производительности. К сожалению, это сообщение не передается в фильтр. В качестве альтернативы я выбрал сообщение WM_Paint, чтобы уменьшить влияние на производительность. Следующий код фильтра создает словарь имен типов форм и количество форм с таким именем. Событие Form.Closed не является надежным при всех условиях закрытия, но событие Disposed выглядит надежным.
internal class FormCreationFilter : IMessageFilter
{
private List<Form> trackedForms = new List<Form>();
internal Dictionary<string, Int32> formCounter = new Dictionary<string, Int32>(); // FormName, CloseCount
public bool PreFilterMessage(ref Message m)
{
// Ideally we would trap the WM_Create, butthe message is not routed through
// the message filter mechanism. It is sent directly to the window.
// Therefore use WM_Paint as a surrgogate filter to prevent the lookup logic
// from running on each message.
const Int32 WM_Paint = 0xF;
if (m.Msg == WM_Paint)
{
Form f = Control.FromChildHandle(m.HWnd) as Form;
if (f != null && !(trackedForms.Contains(f)))
{
trackedForms.Add(f);
f.Disposed += IncrementFormDisposed;
}
}
return false;
}
private void IncrementFormDisposed(object sender, EventArgs e)
{
Form f = sender as Form;
if (f != null)
{
string name = f.GetType().Name;
if (formCounter.ContainsKey(name))
{
formCounter[name] += 1;
}
else
{
formCounter[name] = 1;
}
f.Disposed -= IncrementFormDisposed;
trackedForms.Remove(f);
}
}
}
Создайте экземпляр и установите фильтр, подобный следующему примеру. Цикл foreach
только что показан, чтобы продемонстрировать доступ к счетчику.
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
FormCreationFilter mf = new FormCreationFilter();
Application.AddMessageFilter(mf);
Application.Run(new Form1());
Application.RemoveMessageFilter(mf);
foreach (KeyValuePair<string, Int32> kvp in mf.formCounter)
{
Debug.Print($"{kvp.Key} opened {kvp.Value} times. ");
}
}
Похожие вопросы
Связанные вопросы
Новые вопросы
c#
C # (произносится как «резкий») - это высокоуровневый, статически типизированный язык программирования с несколькими парадигмами, разработанный Microsoft. Код C # обычно нацелен на семейство инструментов и сред выполнения Microsoft .NET, включая, среди прочего, .NET Framework, .NET Core и Xamarin. Используйте этот тег для вопросов о коде, написанном на C # или в формальной спецификации C #.