How can I determine the subsystem used by a given .NET assembly?
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_SUBS开发者_如何学CYSTEM_WINDOWS_CUI*/
}
}
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
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
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";
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.
精彩评论