如果我不想加载主窗体,如何处理 Winforms 中的命令行参数?

Posted

技术标签:

【中文标题】如果我不想加载主窗体,如何处理 Winforms 中的命令行参数?【英文标题】:How do I handle Command Line Arguments in Winforms if I don't want to load Main form? 【发布时间】:2011-12-15 15:43:53 【问题描述】:

我想创建一个行为如下的应用:

    无参数时显示主窗体 在参数“a”上执行一项工作,但未加载主表单。 在参数“b”上,表单使用传递的参数加载(加载该文档)

对于 1 和 3,我可以按如下方式处理表单构造函数中的参数:

public ConfigurationActionManagerForm()

    InitializeComponent();
    Environment.GetCommandLineArgs();
    // do stuff with that argument

但是这种方法不允许我在列表中应用 2. 的行为。

program.cs 中,我甚至可以在创建表单之前对其进行编辑以处理参数,但是如果我不想传递表单,使用Application.Run() 的正确方法是什么?我将如何通知Program 类实例我需要终止或显示出错的消息,甚至显示进程正在执行某些操作的小任务栏图标(将其想象为解压缩过程)。

[STAThread]
static void Main()

    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new ConfigurationActionManagerForm());

来自 MSDN 的 this 方法对我的应用程序是否正确?

【问题讨论】:

【参考方案1】:

我使用微软提供的问题中的示例找到了一个简洁且易于实施的解决方案。

我创建了这个应用程序上下文类,它负责应用程序中的所有内容,我使用它而不是Application.Run() 中的表单,如下所示。 为了实现问题中的行为,我使用了第二种隐藏形式,仅显示任务栏图标。如果用户想查看进程是怎么做的,可以点击任务栏图标,查看日志窗口,其实就是下面例子中的ConfigurationApplierForm

class AnApplicationContext: ApplicationContext

private Form _currentForm;

注意构造函数是私有的,main在这个类里面并且声明为静态的。

private AnApplicationContext()

    Application.ApplicationExit += new EventHandler(this.OnApplicationExit);

    // choose which form to show based on arguments
    if(Environment.GetCommandLineArgs().Contains("-apply"))
    
        _currentForm = new ConfigurationApplierForm();
    
    else
    
        _currentForm = new ConfigurationActionManagerForm();
    

    // initialize the form and attach event handlers
    _currentForm.FormClosed += new FormClosedEventHandler(this.OnCurrentFormClosed);
    _currentForm.ShowDialog();

主要在这里,与原来的有点不同。注意Run 方法中的参数

[STAThread]
static void Main()

    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    // context is passed instead of a form
    Application.Run(new AnApplicationContext()); 


private void OnCurrentFormClosed(object sender, EventArgs e)

    ExitThread();


private void OnApplicationExit(object sender, EventArgs e)

    /* is there anything to do when all forms are closed
    and the application is going to die?*/


另外,我们需要告诉项目这是启动项目。

Project Properties -> Application -> Startup Project

【讨论】:

【参考方案2】:

您的意思是与 Visual Studio 的工作方式相同吗?

如果是这样,那么您不能在普通的 Windows 应用程序中执行此操作 - Visual Studio 作弊。

问题在于,Windows 应用程序既可以是 Windows 窗体应用程序,也可以是控制台应用程序,但不能同时是两者——它在编译时决定(对于 .Net 应用程序,这是在项目属性窗口中)。您的选择是:

使您的应用程序成为 Windows 窗体应用程序

在这种情况下,#1 和#3 将完美运行,但对于#2,您会发现您无法从控制台读取/写入控制台(因为没有控制台!)。如果您的应用程序不需要提供任何反馈,那么这可能没问题 - 像往常一样做您的工作,只是不显示表单:

[STAThread]
static void Main(string[] args)

    if (args.Length > 0)
    
        // Handle #2 here
    
    else
    
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new ConfigurationActionManagerForm());
    

使您的应用程序成为控制台应用程序

在这种情况下,#2 将完美运行,但是尽管 #1 和 #3 将正常运行,但您将始终在后台打开控制台窗口 - 如果您关闭控制台窗口,您的应用程序将结束。

同样,这可能很好,但我个人认为这是一个 hack。

作弊(像 Visual Studio 那样做)

Visual Studio 通过拥有 2 个独立的应用程序来作弊 - 一个是控制台应用程序,另一个是 Windows 窗体应用程序。简单的解决方案是保留它,并要求用户在运行命令行版本时启动不同的可执行文件(例如 myprogram_g.exemyprogram_w.exe)。

Visual Studio 更进一步,只有一个入口点devenv。它通过使用以下事实来做到这一点:出于兼容性原因,如果存在任何歧义,Windows shell 将始终运行.com 文件而不是.exe。当所有快捷方式等指向可执行文件时,如果您在命令行上运行 devenv,则将运行 devenv.com 应用程序,它使用魔法来确定它是作为控制台还是 Windows 应用程序运行。

我的建议是创建两个不同的应用程序并保留它。

更多详情请参阅How do I write a program that can be run either as a console or a GUI application?(请务必阅读有其他有用建议的 cmets)。

另请参阅How to make an application as both GUI and Console application?,了解ildasm 是如何做到这一点的。

【讨论】:

【参考方案3】:

基本上,您需要一个控制台应用程序进行一些更改。

这是一个如何开始使用默认 aboutbox 类的示例:

using System;
using System.Windows.Forms;

namespace ConsoleApplication1

    class Program
    
        static void Main(string[] args)
        
            if (args.Length == 0)
            
                Console.WriteLine("No Arguments");
            
            else
            
                if (args[0] == "a")
                
                    Application.EnableVisualStyles();
                    Application.SetCompatibleTextRenderingDefault(false);
                    Application.Run(new AboutBox1());

                
            

        
    

还有 AboutBox1 类:

using System.Reflection;
using System.Windows.Forms;

namespace ConsoleApplication1

    partial class AboutBox1 : Form
    
        public AboutBox1()
        
            InitializeComponent();
            this.Text = String.Format("About 0 0", AssemblyTitle);
            this.labelProductName.Text = AssemblyProduct;
            this.labelVersion.Text = String.Format("Version 0 0", AssemblyVersion);
            this.labelCopyright.Text = AssemblyCopyright;
            this.labelCompanyName.Text = AssemblyCompany;
            this.textBoxDescription.Text = AssemblyDescription;
        

        #region Assembly Attribute Accessors

        public string AssemblyTitle
        
            get
            
                object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyTitleAttribute), false);
                if (attributes.Length > 0)
                
                    AssemblyTitleAttribute titleAttribute = (AssemblyTitleAttribute)attributes[0];
                    if (titleAttribute.Title != "")
                    
                        return titleAttribute.Title;
                    
                
                return System.IO.Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().CodeBase);
            
        

        public string AssemblyVersion
        
            get
            
                return Assembly.GetExecutingAssembly().GetName().Version.ToString();
            
        

        public string AssemblyDescription
        
            get
            
                object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false);
                if (attributes.Length == 0)
                
                    return "";
                
                return ((AssemblyDescriptionAttribute)attributes[0]).Description;
            
        

        public string AssemblyProduct
        
            get
            
                object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), false);
                if (attributes.Length == 0)
                
                    return "";
                
                return ((AssemblyProductAttribute)attributes[0]).Product;
            
        

        public string AssemblyCopyright
        
            get
            
                object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false);
                if (attributes.Length == 0)
                
                    return "";
                
                return ((AssemblyCopyrightAttribute)attributes[0]).Copyright;
            
        

        public string AssemblyCompany
        
            get
            
                object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), false);
                if (attributes.Length == 0)
                
                    return "";
                
                return ((AssemblyCompanyAttribute)attributes[0]).Company;
            
        
        #endregion

        private void okButton_Click(object sender, EventArgs e)
        
            Close();
        
    

【讨论】:

【参考方案4】:

您可以在没有表单实例的情况下调用 Application.Run()。

这样,它会在不打开表单的情况下启动消息循环。

您也可以在调用 .Run() 之前调用 MessageBox.Show()。

您甚至可以创建并打开一个表单,然后在不指定参数的情况下调用 Run() - 这只是意味着关闭表单不会自动退出应用程序。

例如

        MessageBox.Show("Messaage!");

        Form1 f = new Form1();
        f.Show();

        Application.Run();

如上所述,执行 Run() 的这种方式意味着关闭表单不会自动关闭应用程序。您需要在表单的 Close 事件处理程序中处理此问题。 (Application.Exit())

MSDN 在线可以帮助您解决这个问题 - 检查 Application.Run() 的帮助条目。

【讨论】:

以上是关于如果我不想加载主窗体,如何处理 Winforms 中的命令行参数?的主要内容,如果未能解决你的问题,请参考以下文章

如何处理在WPF TreeView中显示数据的大延迟

如何处理 Nativescript Vue 应用程序上的导航?

如何处理来自大型 ResultSet 的数据而不将它们全部加载到内存中?

如何处理 Windows 媒体播放器控件(即 axWindowsMediaPlayer)然后再次使用它?

如果 DLL 的 app.config 应该在“主配置”中……我们如何处理 DLL 中的 WCF 引用?

如何处理 angular2 延迟加载路由失败