2015/1/7

【C#】重導向(Output Redirection)判斷

參考下面這段程式
1
2
3
4
5
6
7
8
class Program
{
    static void Main(string[] args)
    {
        Console.SetCursorPosition(36, Console.CursorTop);
        Console.WriteLine("Hello World");
    }
}
這段程式能將Hello World印在終端機(Terminal)的行中央,但如果今天使用者使用了重導向將輸出串流導向檔案時,則會看到System.IO.IOException的錯誤例外訊息
Google上找到有人寫了一個ConsoleEx類別可以判斷使用者是否使用了重導向輸出將結果寫到檔案。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static class ConsoleEx
{
    public static bool IsOutputRedirected
    {
        get { return FileType.Char != GetFileType(GetStdHandle(StdHandle.Stdout)); }
    }
    public static bool IsInputRedirected
    {
        get { return FileType.Char != GetFileType(GetStdHandle(StdHandle.Stdin)); }
    }
    public static bool IsErrorRedirected
    {
        get { return FileType.Char != GetFileType(GetStdHandle(StdHandle.Stderr)); }
    }
    private enum FileType { Unknown, Disk, Char, Pipe };
    private enum StdHandle { Stdin = -10, Stdout = -11, Stderr = -12 };
    [DllImport("kernel32.dll")]
    private static extern FileType GetFileType(IntPtr hdl);
    [DllImport("kernel32.dll")]
    static extern bool GetFileSizeEx(IntPtr hFile, out long lpFileSize);
    [DllImport("kernel32.dll")]
    private static extern IntPtr GetStdHandle(StdHandle std);
}
若偵測到使用者重導向則跳過Console相關的控制(如SetCursorPosition(), Clear())以避免發生錯誤。
1
2
3
4
5
6
7
8
9
class Program
{
    static void Main(string[] args)
    {
        if (!ConsoleEx.IsOutputRedirected)
            Console.SetCursorPosition(36, Console.CursorTop);
        Console.WriteLine("Hello World");
    }
}

但上述的ConsoleEx類別提供的方法在使用者將結果輸出到NUL時會判斷錯誤
後來上Stackoverflow詢問得到可以使用GetConsoleMode來判斷終端機模式:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Program
{
    private enum StdHandle { Stdin = -10, Stdout = -11, Stderr = -12 };
    [DllImport("kernel32.dll")]
    private static extern IntPtr GetStdHandle(StdHandle std);
    [DllImport("kernel32.dll")]
    [System.Runtime.Versioning.ResourceExposure(System.Runtime.Versioning.ResourceScope.Process)]
    internal static extern bool GetConsoleMode(IntPtr hConsoleHandle, out int mode);
    public static bool IsRedirected
    {
        get
        {
            int mode;
            GetConsoleMode(GetStdHandle(StdHandle.Stdout), out mode);
            return mode == 0;
        }
    }

    static void Main(string[] args)
    {
        if (!IsRedirected)
            Console.SetCursorPosition(36, Console.CursorTop);
        Console.WriteLine("Hello World");
    }
}

若使用的.Net Framework版本是在4.5以後的話則可以直接使用Console.IsOutputRedirected進行判斷
1
2
3
4
5
6
7
8
9
class Program
{
    static void Main(string[] args)
    {
    if (!Console.IsOutputRedirected)
        Console.SetCursorPosition(36, Console.CursorTop);
        Console.WriteLine("Hello World");
    }
}


Reference:

沒有留言:

張貼留言