加载任何窗体时的运行事件

时间:2019-05-02 15:07:09

标签: c# winforms

我正在尝试在我们的主要前端中创建一个表格流行度竞赛。有许多不再使用的项目,但是要证明要使用和不使用的细节非常困难。

因此,我想到了在加载表单时记录日志的想法,然后在一年左右的时间内,我将进行分组研究,以了解使用哪种表单,使用的频率以及使用的表单。现在的问题是,我不想在每个窗体InitializeComponent块中添加一行。相反,我想将其放在Program.cs文件中,并介绍一些如何拦截所有Form加载,以便可以对其进行记录。

这可能吗?

编辑

使用@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;
        }
    }
}

但是,我想知道是否有一种方法可以获取表单的名称而不是Text。由于这正在访问所有窗口,因此位于托管空间之外,因此我对此表示怀疑。仍然有效,如果没有其他人这样做,我明天将其发布为答案。

4 个答案:

答案 0 :(得分:2)

出于测试或出于比较原因,我正在发布检测和记录Forms活动所需的代码。
如图所示,只需将此代码插入 Main 方法内的 Program.cs 文件中。

此过程记录每个新打开的表单的标题/标题和表单的名称。
可以使用专用方法将其他元素添加到日志中。

当新的Android-FFmpeg事件检测到创建了新窗口时,会将AutomationElement.ProcessId与应用程序的ProcessId进行比较,以确定新窗口是否属于该应用程序。

然后解析Application.OpenForms()集合,使用强制转换为WindowPattern.WindowOpenedEventForm.AccessibleObject来比较AutomationElelement.NativeWindowHandleForm.Handle属性,以避免调用UI用于获取Form句柄的线程(它可以生成异常或线程锁,因为Forms当时只是在加载)。

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 */ }
            });
    }
}

答案 1 :(得分:0)

是的,这应该很容易。对于所有表单和大多数用户控件,都有事件挂钩,例如OnLoad,OnShow,OnClose()。如果您想更细致地了解用户正在使用哪些控件,则可以连接OnClick(),OnMouseOver()和大约一百个其他事件。

...,您可以创建自己的自定义事件。

因此,通过选择表单,然后选择属性(右键单击或F4键)来挂接事件。在顶部的属性窗口中,您有一个“显示事件”按钮,看起来像一个闪电。单击该,然后从列表中选择要用于此日志记录的事件。

enter image description here

答案 2 :(得分:0)

一个不太昂贵(也许)的解决方案可以是:

创建一个新类MyBaseForm,该类继承自System.Windows.Forms.Form,并以您需要的方式处理其load事件。

现在最困难的部分:修改所有现有的表单类,以使它们继承自MyBaseForm,而不继承自默认System.Windows.Forms.Form;并确保您对以后要添加到解决方案中的每个表格都执行相同的操作。

根本不是防弹的,很容易忘记为新表单修改基类和/或错过对现有表单类的修改

但是您可以尝试一下

答案 3 :(得分:0)

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. ");
        }
    }