5

In a C# application, I'd like to determine whether another .NET application is a Console application or not.

Can this be done using the reflection APIs?

EDIT: OK, it doesn't look like I'm going to get a good answer to this question because it doesn't look like the framework exposes the functionality I want. I dug around in the PE/COFF spec and came up with this:

/// <summary>
/// Parses the PE header and determines whether the given assembly is a console application.
/// </summary>
/// <param name="assemblyPath">The path of the assembly to check.</param>
/// <returns>True if the given assembly is a console application; false otherwise.</returns>
/// <remarks>The magic numbers in this method are extracted from the PE/COFF file
/// format specification available from http://www.microsoft.com/whdc/system/platform/firmware/pecoff.mspx
/// </remarks>
bool AssemblyUsesConsoleSubsystem(string assemblyPath)
{
    using (var s = new FileStream(assemblyPath, FileMode.Open, FileAccess.Read))
    {
        var rawPeSignatureOffset = new byte[4];
        s.Seek(0x3c, SeekOrigin.Begin);
        s.Read(rawPeSignatureOffset, 0, 4);
        int peSignatureOffset = rawPeSignatureOffset[0];
        peSignatureOffset |= rawPeSignatureOffset[1] << 8;
        peSignatureOffset |= rawPeSignatureOffset[2] << 16;
        peSignatureOffset |= rawPeSignatureOffset[3] << 24;
        var coffHeader = new byte[24];
        s.Seek(peSignatureOffset, SeekOrigin.Begin);
        s.Read(coffHeader, 0, 24);
        byte[] signature = {(byte)'P', (byte)'E', (byte)'\0', (byte)'\0'};
        for (int index = 0; index < 4; index++)
        {
            Assert.That(coffHeader[index], Is.EqualTo(signature[index]),
                "Attempted to check a non PE file for the console subsystem!");
        }
        var subsystemBytes = new byte[2];
        s.Seek(68, SeekOrigin.Current);
        s.Read(subsystemBytes, 0, 2);
        int subSystem = subsystemBytes[0] | subsystemBytes[1] << 8;
        return subSystem == 3; /*IMAGE_SUBSYSTEM_WINDOWS_CUI*/
    }
}
Billy ONeal
  • 104,103
  • 58
  • 317
  • 552

4 Answers4

4

This is out of the scope of managed code. From the .NET perspective, Console and Windows UI applications are the same. You have to peek into the PE file header. Search for the word, "Subsystem" on this page http://msdn.microsoft.com/en-us/magazine/bb985997.aspx

Rob Scott
  • 7,921
  • 5
  • 38
  • 63
Andrey
  • 59,039
  • 12
  • 119
  • 163
  • Yes -- in native code I usually use the dbghelp apis for this, but I'm not in native code :( – Billy ONeal Jun 24 '10 at 16:14
  • @Billy ONeal the structure of headers is very simple and well known. it takes few lines of code in c++ (+ headers) to find out the subsystem. i guess in c# i should not take much more. – Andrey Jun 25 '10 at 10:26
1

The SHGetFileInfo function can do this:

[DllImport("shell32.dll", CharSet=CharSet.Auto, EntryPoint="SHGetFileInfo")]
public static extern ExeType GetExeType(string pszPath, uint dwFileAttributes = 0, IntPtr psfi = default(IntPtr), uint cbFileInfo = 0, uint uFlags = 0x2000);

[Flags]
public enum ExeType
{
    None = 0,
    WinNT = 0x04000000,
    PE = ((int)'P') | ((int)'E' << 8),
    NE = ((int)'N') | ((int)'E' << 8),
    MZ = ((int)'M') | ((int)'Z' << 8),
}

Then, according to the specification, if it's only MZ or PE, it is opened in console, otherwise (if a version is specified), it is open in a window.

ExeType type = GetExeType("program.exe");
if(type == ExeType.PE || type == ExeType.MZ) return "console";
else return "window";
Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
IS4
  • 11,945
  • 2
  • 47
  • 86
  • -1: PEs can still use the console subsystem -- you would need to check the subsystem part in the PE headers. – Billy ONeal Nov 08 '14 at 16:51
  • (I mean, NE and LE can only be console apps; PE can be one of all 3 subsystems. – Billy ONeal Nov 08 '14 at 16:54
  • @BillyONeal And who says they don't? The specification says (basically) if there's no version number in the return code (`ExeType` here), it is a console application. – IS4 Nov 08 '14 at 18:36
  • Just a little clarification regarding the ExeType enum. The definition above obscures the fact that the return type from `SHGetFileInfo` function is a `DWORD_PTR` and a vital part of the test is that the high order word must be 0 (this is the "no version number" test) so to be more descriptive the enum names could be changed to PE_Console and MZ_Console and a descriptive comment such as `// high word == 0 means console subsystem`. – Uber Kluger Jul 08 '20 at 03:00
1

I suppose it should the same as for native apps so then you might be able to adapt this article from C++ to C# to read the PE headers: How To Determine Whether an Application is Console or GUI

Hans Olsson
  • 54,199
  • 15
  • 94
  • 116
0

I don't think there's a scientific way to determine it, the closest workaround that comes to my mind is using reflection to check if the application references and loads the WinForms assembly, but I'm not entirely sure. Might give it a try.

Matteo Mosca
  • 7,380
  • 4
  • 44
  • 80
  • 1
    you can't definitely find it out using reflection. you should inspect the exe file header. – Andrey Jun 24 '10 at 16:07
  • My "anwser" was obviously thought when you have access to the program you are inspecting, not the compiled exe. Sorry for the misunderstanding. – Matteo Mosca Jun 24 '10 at 16:11
  • That won't work because you could use WinForms (or WPF) from a console application, and a non-console application does not need to use WinForms (for instance, a Windows service uses neither). Checking the .exe header is the only way. – Ruben Jun 24 '10 at 16:21