在后台运行Mono应用程序时,我能否检测到缺少控制台?

时间:2014-08-07 10:32:44

标签: c# mono

我有一个.NET 4.5控制台应用程序,我使用Mono在CentOS上运行。代码完成:

Console.ReadLine();

如果我以交互方式运行应用程序,那么它的行为与我预期的一样,Console.ReadLine()等待键盘输入,但是,如果我使用nohup运行应用程序在后台运行它...

nohup mono Program.exe > Program.log &

然后Program.log显示Console.ReadLine()导致奇怪的异常:

System.UnauthorizedAccessException: Access to the path "/home/user/[Unknown]" is denied.
  at System.IO.FileStream.ReadData (IntPtr handle, System.Byte[] buf, Int32 offset, Int32 count) [0x00000] in <filename unknown>:0
  at System.IO.FileStream.ReadInternal (System.Byte[] dest, Int32 offset, Int32 count) [0x00000] in <filename unknown>:0
  at System.IO.FileStream.Read (System.Byte[] array, Int32 offset, Int32 count) [0x00000] in <filename unknown>:0
  at System.IO.StreamReader.ReadBuffer () [0x00000] in <filename unknown>:0
  at System.IO.StreamReader.ReadLine () [0x00000] in <filename unknown>:0
  at System.IO.UnexceptionalStreamReader.ReadLine () [0x00000] in <filename unknown>:0
  at System.IO.SynchronizedReader.ReadLine () [0x00000] in <filename unknown>:0
  at System.Console.ReadLine () [0x00000] in <filename unknown>:0
  at Program1.Program.Main (System.String[] args) [0x00000] in <filename unknown>:0

我显然可以抓住并静静地忽略该异常,但我很想知道是否有可能检测到我没有控制台这样的事实,从而改变了我的应用程序的行为?

3 个答案:

答案 0 :(得分:2)

经过一些实验后,这似乎可以帮我完成工作:

if (Console.In is StreamReader) {
    Console.WriteLine("Interactive"); 
} else {
    Console.WriteLine("Background"); 
}

不确定它是否适用于所有可能的stdin重定向等等,但它对我的目的来说效果很好。

答案 1 :(得分:1)

您可以查看 - Environment.UserInteractive Property(单声道也支持{ - 3}})

  

对于Windows进程或a,UserInteractive属性报告为false   IIS之类的服务,无需用户界面即可运行。如果这个属性   是假的,不要显示模态对话框或消息框,因为那里   没有用户与之交互的图形用户界面。

答案 2 :(得分:0)

基本上我通常想知道的是:

  • 是否作为服务运行
  • 是否作为控制台运行

我想你的问题也是关于这一点......我只会写下食谱,因为我花了太长时间才弄明白所有的细节......

设置项目

最简单的方法是创建一个windows service C#应用程序,然后开始攻击Program.cs

基本上我在这里做的是:

  • 如果是Mono,请检查TTY是否已附加 - &gt;控制台,单声道
  • 如果是Windows,请检查UserInteractive
  • 如果它是交互式的,请分配一个控制台,以便该工作正常。
  • 如果这不起作用,我们只假设一个控制台应用程序。

以下是代码:

public static ProgramType ProgramType
{
    get
    {
        if (!programType.HasValue)
        {
            try
            {
                if (Type.GetType("Mono.Runtime") != null)
                {
                    // It's a console application if 'bool Mono.Unix.Native.Syscall.isatty(0)' in Mono.Posix.dll
                    var monoPosix = System.Reflection.Assembly.Load("Mono.Posix, Version=4.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756");
                    Type syscallType = monoPosix.GetType("Mono.Unix.Native.Syscall");
                    var method = syscallType.GetMethod("isatty");
                    bool isatty = (bool)method.Invoke(null, new object[] { 0 });

                    if (isatty)
                    {
                        programType = ProgramType.MonoConsole;
                    }
                    else
                    {
                        programType = ProgramType.MonoService;
                    }
                }
                else
                {
                    if (Environment.UserInteractive)
                    {
                        programType = ProgramType.WindowsConsole;
                    }
                    else
                    {
                        programType = ProgramType.WindowsService;
                    }
                }
            }
            catch
            {
                programType = ProgramType.Unknown;
            }
        }
        return programType.Value;
    }
}

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AllocConsole();

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool FreeConsole();

[DllImport("kernel32", SetLastError = true)]
static extern bool AttachConsole(int dwProcessId);

[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();

[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);


/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
    switch (ProgramType)
    {
        case ProgramType.WindowsConsole:
            {
                // WindowsConsole application
                //
                // Get a pointer to the forground window. The idea here is that
                // IF the user is starting our application from an existing console
                // shell, that shell will be the uppermost window. We'll get it
                // and attach to it

                IntPtr ptr = GetForegroundWindow();

                int u;
                GetWindowThreadProcessId(ptr, out u);

                try
                {
                    Process process = Process.GetProcessById(u);

                    if (process.ProcessName == "cmd")
                    {
                        // Is the uppermost window a cmd process?
                        AttachConsole(process.Id);
                    }
                    else
                    {
                        // No console AND we're in console mode ... create a new console.
                        AllocConsole();
                    }

                    // Console is now accessible.
                    NubiloSoft.Util.Logging.Sink.Console.Register();

                    // Arguments?

                    StartConsoleService();
                }
                finally
                {
                    FreeConsole();
                }
            }
            break;

        case ProgramType.MonoConsole:
            {
                // Console is now accessible.
                NubiloSoft.Util.Logging.Sink.Console.Register();

                // Arguments?
                StartConsoleService();
            }
            break;

        case ProgramType.MonoService:
        case ProgramType.WindowsService:
            {
                // Start service
                ServiceBase[] ServicesToRun;
                ServicesToRun = new ServiceBase[]
                {
                    new Service1()
                };
                ServiceBase.Run(ServicesToRun);
            }
            break;

        default:
            Console.WriteLine("Unknown CLR detected. Running as console.");
            {
                // Console is now accessible.
                NubiloSoft.Util.Logging.Sink.Console.Register();

                // Arguments?
                StartConsoleService();
            }
            break;
    }
}

我通常使用默认的Service1在某个静态Startup类上调用'Start'和'Stop'。它与StartConsoleService的作用相同。由于暴露了ProgramType,因此您可以在需要时使用它。