代码之家  ›  专栏  ›  技术社区  ›  Jaster

无法连接控制台

  •  0
  • Jaster  · 技术社区  · 6 年前

    我正在尝试将控制台连接到服务,如果im 从调试器运行它 . 我读过一些“有效”的解决方案,但这些似乎并不真正有效。以下是我使用的代码:

        public static void RunService(Func<ServiceBase> factory)
        {
            if (Debugger.IsAttached)
            {
                Utils.AttachConsole();
                Console.Write($"Starting service ");
                var instance = factory();
                Console.WriteLine(instance.GetType().Name);
                //Invoke start Method
                Console.WriteLine("Press [ENTER] to exit");
                Console.ReadLine();
                //Stop service
            }
            else
            {
                ServiceBase.Run(factory());
            }
        }
    

        public static void AttachConsole()
        {
            var ret = NativeMethods.AllocConsole();
            IntPtr currentStdout = NativeMethods.GetStdHandle(NativeMethods.STD_OUTPUT_HANDLE);
            NativeMethods.SetStdHandle(NativeMethods.STD_OUTPUT_HANDLE, new IntPtr(7));
            TextWriter writer = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true };
            Console.SetOut(writer);
        }
    

    互操作包括:

    internal static class NativeMethods
    {
        internal const uint STD_OUTPUT_HANDLE = 0xFFFFFFF5;
    
        [DllImport("kernel32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool AllocConsole();
    
        [DllImport("kernel32.dll")]
        internal static extern IntPtr GetStdHandle(uint nStdHandle);
    
        [DllImport("kernel32.dll")]
        internal static extern void SetStdHandle(uint nStdHandle, IntPtr handle);
    }
    

    编辑:

    编辑 就为了汉斯 -这是“完整代码”

    static void Main(string[] args)
    {
        ServiceLauncher.RunService(() => new FactoryService();
    }
    

    项目设置为应用程序类型windows。

    3 回复  |  直到 6 年前
        1
  •  2
  •   Dave Smash    6 年前

    我能够复制你的问题,并让它在我的机器上工作。您的一些代码看起来像是来自于 No console output when using AllocConsole and target architecture x86 . 如果您阅读该答案下的评论线程,您将看到 new IntPtr(7) 从Windows 7/Server 2012起不起作用。Windows 7的“新幻数”对我也不起作用。为了解决这个问题,我开始了从注释中把给定的C++调用移植到C语言中的路径,这需要对PInvokes进行一些签名修改(这些都是从PInvoke.net复制并粘贴的,所以它们应该很好)。我所做的更改几乎完全在PInvoke代码中。以下是完整的工作代码集:

    Program.cs(未更改):

    static void Main()
    {
        ServiceLauncher.RunService(() => new Service1());
    }
    

    ServiceLauncher.cs(未更改):

    public static void RunService(Func<ServiceBase> factory)
    {
        if (Debugger.IsAttached)
        {
            Utils.AttachConsole();
            Console.Write($"Starting service ");
            var instance = factory();
            Console.WriteLine(instance.GetType().Name);
            //Invoke start Method
            Console.WriteLine("Press [ENTER] to exit");
            Console.ReadLine();
            //Stop service
        }
        else
        {
            ServiceBase.Run(factory());
        }
    }
    

    public static void AttachConsole()
    {
        var ret = NativeMethods.AllocConsole();
        IntPtr currentStdout = NativeMethods.GetStdHandle(NativeMethods.STD_OUTPUT_HANDLE);
    
        // IntPtr(7) was a dangerous assumption that doesn't work on current versions of Windows...
        //NativeMethods.SetStdHandle(NativeMethods.STD_OUTPUT_HANDLE, new IntPtr(7));
    
        // Instead, get the defaultStdOut using PInvoke
        SafeFileHandle defaultStdOut = NativeMethods.CreateFile("CONOUT$", EFileAccess.GenericRead | EFileAccess.GenericWrite, EFileShare.Write, IntPtr.Zero, ECreationDisposition.OpenExisting, 0, IntPtr.Zero);
        NativeMethods.SetStdHandle(NativeMethods.STD_OUTPUT_HANDLE, defaultStdOut.DangerousGetHandle());    // also seems dangerous... there may be an alternate signature for SetStdHandle that takes SafeFileHandle.
    
        TextWriter writer = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true };
        Console.SetOut(writer);
    }
    

    NativeMethods.cs(几乎完全不同-评论中给出了链接和解释)。此文件中包含的枚举(在类范围之外),但可以根据您的判断移动到不同的文件:

    internal static class NativeMethods
    {
        // 0xFFFFFFF5 is not consistent with what I found...
        //internal const uint STD_OUTPUT_HANDLE = 0xFFFFFFF5; 
    
        // https://www.pinvoke.net/default.aspx/kernel32.getstdhandle
        internal const int STD_OUTPUT_HANDLE = -11;
    
        [DllImport("kernel32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool AllocConsole();
    
        // method signature changed per https://www.pinvoke.net/default.aspx/kernel32.getstdhandle
        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern IntPtr GetStdHandle(int nStdHandle);
    
        // method signature changed per https://www.pinvoke.net/default.aspx/kernel32.setstdhandle
        [DllImport("kernel32.dll")]
        internal static extern bool SetStdHandle(int nStdHandle, IntPtr hHandle);
    
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        internal static extern SafeFileHandle CreateFile(
            string lpFileName,
            EFileAccess dwDesiredAccess,
            EFileShare dwShareMode,
            IntPtr lpSecurityAttributes,
            ECreationDisposition dwCreationDisposition,
            EFileAttributes dwFlagsAndAttributes,
            IntPtr hTemplateFile);
    }
    
    
    // ENUMS FROM http://www.pinvoke.net/default.aspx/kernel32/CreateFile.html
    [Flags]
    public enum EFileAccess : uint
    {
        //
        // Standart Section
        //
    
        AccessSystemSecurity = 0x1000000,   // AccessSystemAcl access type
        MaximumAllowed = 0x2000000,     // MaximumAllowed access type
    
        Delete = 0x10000,
        ReadControl = 0x20000,
        WriteDAC = 0x40000,
        WriteOwner = 0x80000,
        Synchronize = 0x100000,
    
        StandardRightsRequired = 0xF0000,
        StandardRightsRead = ReadControl,
        StandardRightsWrite = ReadControl,
        StandardRightsExecute = ReadControl,
        StandardRightsAll = 0x1F0000,
        SpecificRightsAll = 0xFFFF,
    
        FILE_READ_DATA = 0x0001,        // file & pipe
        FILE_LIST_DIRECTORY = 0x0001,       // directory
        FILE_WRITE_DATA = 0x0002,       // file & pipe
        FILE_ADD_FILE = 0x0002,         // directory
        FILE_APPEND_DATA = 0x0004,      // file
        FILE_ADD_SUBDIRECTORY = 0x0004,     // directory
        FILE_CREATE_PIPE_INSTANCE = 0x0004, // named pipe
        FILE_READ_EA = 0x0008,          // file & directory
        FILE_WRITE_EA = 0x0010,         // file & directory
        FILE_EXECUTE = 0x0020,          // file
        FILE_TRAVERSE = 0x0020,         // directory
        FILE_DELETE_CHILD = 0x0040,     // directory
        FILE_READ_ATTRIBUTES = 0x0080,      // all
        FILE_WRITE_ATTRIBUTES = 0x0100,     // all
    
        //
        // Generic Section
        //
    
        GenericRead = 0x80000000,
        GenericWrite = 0x40000000,
        GenericExecute = 0x20000000,
        GenericAll = 0x10000000,
    
        SPECIFIC_RIGHTS_ALL = 0x00FFFF,
        FILE_ALL_ACCESS =
        StandardRightsRequired |
        Synchronize |
        0x1FF,
    
        FILE_GENERIC_READ =
        StandardRightsRead |
        FILE_READ_DATA |
        FILE_READ_ATTRIBUTES |
        FILE_READ_EA |
        Synchronize,
    
        FILE_GENERIC_WRITE =
        StandardRightsWrite |
        FILE_WRITE_DATA |
        FILE_WRITE_ATTRIBUTES |
        FILE_WRITE_EA |
        FILE_APPEND_DATA |
        Synchronize,
    
        FILE_GENERIC_EXECUTE =
        StandardRightsExecute |
            FILE_READ_ATTRIBUTES |
            FILE_EXECUTE |
            Synchronize
    }
    
    [Flags]
    public enum EFileShare : uint
    {
        /// <summary>
        /// 
        /// </summary>
        None = 0x00000000,
        /// <summary>
        /// Enables subsequent open operations on an object to request read access. 
        /// Otherwise, other processes cannot open the object if they request read access. 
        /// If this flag is not specified, but the object has been opened for read access, the function fails.
        /// </summary>
        Read = 0x00000001,
        /// <summary>
        /// Enables subsequent open operations on an object to request write access. 
        /// Otherwise, other processes cannot open the object if they request write access. 
        /// If this flag is not specified, but the object has been opened for write access, the function fails.
        /// </summary>
        Write = 0x00000002,
        /// <summary>
        /// Enables subsequent open operations on an object to request delete access. 
        /// Otherwise, other processes cannot open the object if they request delete access.
        /// If this flag is not specified, but the object has been opened for delete access, the function fails.
        /// </summary>
        Delete = 0x00000004
    }
    
    public enum ECreationDisposition : uint
    {
        /// <summary>
        /// Creates a new file. The function fails if a specified file exists.
        /// </summary>
        New = 1,
        /// <summary>
        /// Creates a new file, always. 
        /// If a file exists, the function overwrites the file, clears the existing attributes, combines the specified file attributes, 
        /// and flags with FILE_ATTRIBUTE_ARCHIVE, but does not set the security descriptor that the SECURITY_ATTRIBUTES structure specifies.
        /// </summary>
        CreateAlways = 2,
        /// <summary>
        /// Opens a file. The function fails if the file does not exist. 
        /// </summary>
        OpenExisting = 3,
        /// <summary>
        /// Opens a file, always. 
        /// If a file does not exist, the function creates a file as if dwCreationDisposition is CREATE_NEW.
        /// </summary>
        OpenAlways = 4,
        /// <summary>
        /// Opens a file and truncates it so that its size is 0 (zero) bytes. The function fails if the file does not exist.
        /// The calling process must open the file with the GENERIC_WRITE access right. 
        /// </summary>
        TruncateExisting = 5
    }
    
    [Flags]
    public enum EFileAttributes : uint
    {
        Readonly = 0x00000001,
        Hidden = 0x00000002,
        System = 0x00000004,
        Directory = 0x00000010,
        Archive = 0x00000020,
        Device = 0x00000040,
        Normal = 0x00000080,
        Temporary = 0x00000100,
        SparseFile = 0x00000200,
        ReparsePoint = 0x00000400,
        Compressed = 0x00000800,
        Offline = 0x00001000,
        NotContentIndexed = 0x00002000,
        Encrypted = 0x00004000,
        Write_Through = 0x80000000,
        Overlapped = 0x40000000,
        NoBuffering = 0x20000000,
        RandomAccess = 0x10000000,
        SequentialScan = 0x08000000,
        DeleteOnClose = 0x04000000,
        BackupSemantics = 0x02000000,
        PosixSemantics = 0x01000000,
        OpenReparsePoint = 0x00200000,
        OpenNoRecall = 0x00100000,
        FirstPipeInstance = 0x00080000
    }
    
        2
  •  0
  •   Archlight    6 年前

    我为我开发的每个windows服务都这样做。但通常我使用winform,甚至在某些情况下使用名为pipes的管道,因为它能够做比在控制台中查看输出更有趣的事情。

    1) 创建windows服务项目

    2) 将项目输出更改为控制台应用程序。

    3) 将“Service1”类更改为此。

    using System.ServiceProcess;
    
    namespace WindowsService1
    {
        public partial class Service1 : ServiceBase
        {
            readonly Runner _runner = new Runner();
            static void Main(string[] args)
            {
                var service = new Service1();
                if (Debugger.IsAttached)
                {
                    service.OnStart(args);
                    Console.WriteLine("Find the any key!");
                    Console.Read();
                    service.OnStop();
                }
                else
                {
                    ServiceBase[] ServicesToRun;
                    ServicesToRun = new ServiceBase[]
                    {
                        service
                    };
                    ServiceBase.Run(ServicesToRun);
                }
            }
    
            public Service1()
            {
                InitializeComponent();
            }
    
            protected override void OnStart(string[] args)
            {
                _runner.Run();
            }
    
            protected override void OnStop()
            {
                _runner.Stop();
            }
        }
    }
    

    然后,在Runner类中,您可以根据工厂方法执行任何操作。

        3
  •  0
  •   solidau    6 年前