我正在尝试在我们的主前端创建一个流行度竞赛表单。有许多项目不再使用,但获得哪些使用和哪些不再使用的详细信息被证明是困难的。
所以我想到了在窗体加载时记录它的想法,然后在一年左右的时间里,我将运行一个组,并了解哪些窗体被使用,使用频率和使用者。现在的问题是,我不想在每个窗体的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;
}
}
}
然而,我想知道是否有一种方法可以获得表单的名称,而不是它的文本。因为这是访问所有窗口,因此是在托管空间之外,我怀疑它。尽管如此,它的工作,我会张贴这作为一个答案,明天如果没有人这样做。
4条答案
按热度按时间kwvwclae1#
为了测试或比较的原因,我发布了检测和记录表单活动所需的代码。
如图所示,此代码只需插入到**
Program.cs
文件中的Main**方法内。本程序记录每个新打开的表格的标题/题注和表格名称。
可以使用专用方法将其他元素添加到日志中。
当新WindowPattern.WindowOpenedEvent事件检测到创建了新窗口时,将
AutomationElement.ProcessId
与应用程序的ProcessId进行比较,以确定新窗口是否属于应用程序。然后解析
Application.OpenForms()
集合,使用Form.AccessibleObject到Control.ControlAccessibleObject的强制转换来比较AutomationElelement.NativeWindowHandle
与Form.Handle
属性,以避免调用UI线程来获取窗体的句柄(这可能会生成异常或线程锁,因为窗体此时才加载)。suzh9iv82#
是的,这应该很简单。所有表单和大多数用户控件都有OnLoad、OnShow、OnClose()等事件挂钩。如果你想在更细的层次上看到你的用户正在使用哪些控件,你可以挂钩OnClick()、OnMouseOver()和大约一百个其他事件。
...您可以创建自己的自定义事件。
因此,通过选择表单,然后选择属性(右键单击或F4键)来连接事件。在顶部的属性窗口中,您有一个“show events”按钮,看起来像一个 lightning 。单击该按钮,然后从列表中选择您想要用于此日志记录的事件。
9rygscc13#
一个不那么昂贵(也许)的解决方案可以是这样的:
创建一个新的类
MyBaseForm
,它继承自System.Windows.Forms.Form
,并以您需要的方式处理其load事件。现在是最难的部分:修改所有现有的表单类,使它们继承自
MyBaseForm
,而不是默认的System.Windows.Forms.Form
;并确保对将来要添加到解决方案中的每个窗体执行相同的操作。完全不是防弹的,很容易忘记修改新窗体的基类和/或错过对现有窗体类的修改
但你可以给予一试
lbsnaicq4#
将IMessageFilter应用于应用程序以检测WM_Create消息,然后确定目标句柄是否属于
Form
,这将是性能影响最小的理想解决方案。遗憾的是,该消息不会传递到筛选器。作为替代方案,我已选择WM_Paint消息以降低性能影响。以下筛选器代码创建窗体类型名称的字典和Form 's。Form.Closed Event并非在所有关闭条件下都可靠,但Disposed事件似乎可靠。创建一个示例并安装过滤器,如下例所示,
foreach
循环只是演示如何访问计数。