Запустить событие при загрузке любой формы - PullRequest
1 голос
/ 02 мая 2019

Я пытаюсь создать Конкурс популярности для форм в нашем основном интерфейсе. Есть много предметов, которые больше не используются, но получение сведений о том, какие из них больше не используются, оказывается трудным.

Поэтому мне пришла в голову идея регистрировать форму, когда она загружается, а затем через год или около того я буду руководить группой и узнавать, какие формы используются, как часто и кем. Теперь проблема в том, что я не хочу добавлять строку в каждый блок InitializeComponent форм. Вместо этого я хотел бы поместить это в файл Program.cs и кое-как перехватить все загрузки форм, чтобы я мог их записывать.

Возможно ли это?

Редактировать

Используя комментарий @ Джими, я смог придумать следующее.

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;
        }
    }
}

Однако мне интересно, есть ли способ получить имя формы вместо ее Текст. Так как он обращается ко ВСЕМ окнам и поэтому находится за пределами управляемого пространства, я сомневаюсь в этом. Тем не менее, это работает, и я опубликую это как ответ завтра, если никто не сделает этого.

Ответы [ 4 ]

2 голосов
/ 02 мая 2019

Я публикую код, необходимый для обнаружения и регистрации активности форм, для тестирования или для сравнения.
Как показано, этот код нужно только вставить в 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;
using System.Windows.Forms;

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>()
                            .Where(f => (f.AccessibilityObject as Control.ControlAccessibleObject).Handle == elmHandle)
                            .FirstOrDefault();

                        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 */ }
            });
    }
}
0 голосов
/ 02 мая 2019

Не очень дорогое (может быть) решение может быть таким:

Создайте новый класс MyBaseForm, который наследуется от System.Windows.Forms.Form, и обрабатывайте его событие загрузки так, как вам нужно.

Теперь сложная часть: измените все существующие классы форм, чтобы они наследовали от MyBaseForm, а не от значения по умолчанию System.Windows.Forms.Form; и убедитесь, что вы делаете то же самое для каждой будущей формы, которую вы добавите к своему решению.

Не является пуленепробиваемым, может быть легко забыть изменить базовый класс для новой формы и / или пропустить модификацию для существующего класса формы

Но вы можете попробовать

0 голосов
/ 02 мая 2019

Применение 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. ");
        }
    }
0 голосов
/ 02 мая 2019

Да, это должно быть легко. Существуют перехватчики событий, такие как OnLoad, OnShow, OnClose () для всех форм и большинства пользовательских элементов управления. Если вы хотите увидеть на более детальном уровне, какие элементы управления используются вашими пользователями, вы можете подключить OnClick (), OnMouseOver () и около сотни других событий.

... и вы можете создавать свои собственные события.

Итак, подключите события, выбрав форму, а затем свойства (щелчок правой кнопкой или клавиша F4). В окне свойств в верхней части у вас есть кнопка «Показать события», которая выглядит как молния. Нажмите на это, а затем выберите из списка событие, которое вы хотите использовать для этой регистрации.

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...