Wpf和命令行应用程序在同一个可执行文件中

时间:2015-08-25 12:35:23

标签: c# wpf command-line

我希望有一个可执行文件可用于打开图形应用程序(默认用例,单击.exe时),或者我可以用来运行命令行任务。

这可能吗?

如果是这样,我将如何修改我的app.xaml / app.xaml.cs,以便它只打开特定条件的图形视图(例如没有命令行参数)?

4 个答案:

答案 0 :(得分:2)

您可以检查应用程序是否已从控制台执行。如果没有,您可以动态分配控制台:

if (GetConsoleWindow() == IntPtr.Zero)
   AllocConsole();

,其中

[DllImport("kernel32.dll")]
public static extern IntPtr GetConsoleWindow();

[DllImport("kernel32.dll")]
public static extern bool AllocConsole();

答案 1 :(得分:2)

@ BrunoKlein的回答将起作用,我的答案基于他的解决方案。引用@BrunoKlein,

  

首先,您必须使用WPF应用程序项目并更改app.xaml,以便您可以覆盖窗口创建。

<Application x:Class="WpfApplication1.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfApplication1">
    <Application.Resources>

    </Application.Resources>
</Application>
  

请注意,这缺少StartupUri属性。

现在,更简单(至少在Visual Studio 2015中有效),转到项目属性,并将输出类型从Windows应用程序更改为控制台应用程序。这使得项目构建为控制台应用程序,但仍具有Windows应用程序的功能。

Windows Application (此照片中会突出显示类库,而是选择控制台应用程序)

你做到了!完成。

现在,您的“主要”方法不是拥有void Main(string[] args),而是自动生成的OnStautup类的App方法:

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        string[] args = e.Args;

        if (SomeConditionBasedOnTheArgs(args))
        {
            // Instantiate view, call View.Show()
        }
        else
        {
            // Process the args
        }
    }
}

请注意,这个答案和@ BrunoKlein的答案之间的一个区别是,如果从explorer / start菜单/桌面运行它,这个将始终“显示”一个控制台,但如果从命令行运行,它将运行并指示所有标准输出到该控制台,就像任何普通的控制台应用程序一样。

答案 2 :(得分:0)

使用以上两种解决方案时,我一直遇到错误。在此处进行描述,并以相同的方式解决。 Visual Studio 2017 Debug Error: To prevent an unsafe abort when evaluating the function *.toString all threads where allowed to run

工具→选项→调试→常规→选中“使用托管的兼容模式”

继续寻找不需要关闭某些调试选项的解决方案,我发现本文对我来说非常有效。 https://benohead.com/blog/2015/04/22/c-wpf-console-hybrid-application/

应该删除它,添加一个Program.cs,将Windows App交换到控制台应用程序,并使App.Program成为启动对象。然后在Program.cs中,添加并调整以下代码。 DLL导入会确保能够隐藏控制台窗口。 所有的功劳归@Benohead。

我将默认状态更改为GUI模式,并使参数触发了控制台应用程序。 控制台窗口将显示并在进入GUI模式之前不久关闭。

using System;
using System.Runtime.InteropServices;

namespace HybridApp
{
    public class Program
    {
        [DllImport("kernel32.dll")]
        private static extern IntPtr GetConsoleWindow();

        [DllImport("user32.dll")]
        private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

        [STAThread]
        public static void Main(string[] args)
        {
            if (args.Length == 0)
            {
                // GUI mode
                ShowWindow(GetConsoleWindow(), 0 /*SW_HIDE*/);
                App.Main();
            }
            else if (args.Length > 0 && args[0] == "-c")
            {
                // console mode
                Console.WriteLine("Console mode active!");
                Console.ReadLine();
            }
        }
    }
}

答案 3 :(得分:0)

正如@BrunoKlein 建议的那样,我从 StartupUri 中删除了 App.xml 属性,然后覆盖了 OnStartup 方法。但是我改用 AttachConsole,因为我发现 AllocConsole 在从命令提示符运行时会导致出现一个额外的控制台窗口。

调用 FreeConsoleShutdown 以彻底退出也很重要。

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        if (e.Args.Length == 0)
        {
            // No command line arguments, so run as normal Windows application.
            var mainWindow = new MainWindow();
            mainWindow.ShowDialog();
        }
        else
        {
            // Command-line arguments were supplied, so run in console mode.
            try
            {
                const int ATTACH_PARENT_PROCESS = -1;
                if (AttachConsole(ATTACH_PARENT_PROCESS))
                {
                    CommandLineVersionOfApp.ConsoleMain(e.Args);
                }
            }
            finally
            {
                FreeConsole();
                Shutdown();
            }
        }
    }

    [DllImport("kernel32")]
    private static extern bool AttachConsole(int dwProcessId);

    [DllImport("kernel32")]
    private static extern bool FreeConsole();
}