【问题标题】:How can I determine the subsystem used by a given .NET assembly?如何确定给定 .NET 程序集使用的子系统?
【发布时间】:2020-07-08 13:53:24
【问题描述】:

在 C# 应用程序中,我想确定另一个 .NET 应用程序是否是控制台应用程序。

这可以使用反射 API 来完成吗?

编辑:好的,看起来我不会得到这个问题的好答案,因为它看起来不像框架公开了我想要的功能。我在 PE/COFF 规范中进行了研究,并提出了这个:

/// <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*/
    }
}

【问题讨论】:

    标签: c# reflection


    【解决方案1】:

    这超出了托管代码的范围。从 .NET 的角度来看,控制台和 Windows UI 应用程序是相同的。您必须查看 PE 文件头。在此页面上搜索单词“子系统”http://msdn.microsoft.com/en-us/magazine/bb985997.aspx

    【讨论】:

    • 是的——在本机代码中,我通常为此使用 dbghelp api,但我不在本机代码中:(
    • @Billy ONeal 标头的结构非常简单且众所周知。只需几行 C++ 代码(+ 标头)即可找出子系统。我想在 c# 中我不应该花更多的时间。
    【解决方案2】:

    我想它应该与原生应用程序相同,因此您可以将这篇文章从 C++ 改编为 C# 以读取 PE 标头:How To Determine Whether an Application is Console or GUI

    【讨论】:

      【解决方案3】:

      SHGetFileInfo 函数可以做到这一点:

      [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),
      }
      

      那么按照规范,如果只有MZ或PE,则在控制台打开,否则(如果指定版本),则在窗口中打开。

      ExeType type = GetExeType("program.exe");
      if(type == ExeType.PE || type == ExeType.MZ) return "console";
      else return "window";
      

      【讨论】:

      • -1:PE 仍然可以使用控制台子系统——您需要检查 PE 标头中的子系统部分。
      • (我的意思是,NE 和 LE 只能是控制台应用程序;PE 可以是所有 3 个子系统之一。
      • @BillyONEal 谁说他们没有?规范说(基本上)如果返回码中没有版本号(ExeType 这里),它是一个控制台应用程序。
      • @BillyONEal 感谢您的示例。 ☺
      • 关于 ExeType 枚举的一点说明。上面的定义掩盖了 SHGetFileInfo 函数的返回类型是 DWORD_PTR 的事实,并且测试的一个重要部分是高位字必须为 0(这是“无版本号”测试),因此更具描述性的枚举名称可以更改为 PE_Console 和 MZ_Console 以及描述性注释,例如 // high word == 0 means console subsystem
      【解决方案4】:

      我认为没有科学的方法来确定它,我想到的最接近的解决方法是使用反射来检查应用程序是否引用并加载 WinForms 程序集,但我并不完全确定。可以试试看。

      【讨论】:

      • 您不能绝对使用反射找到它。您应该检查 exe 文件头。
      • 我的“anwser”显然是当您可以访问您正在检查的程序时想到的,而不是编译的 exe。很抱歉造成误解。
      • 这行不通,因为您可以从控制台应用程序使用 WinForms(或 WPF),而非控制台应用程序不需要使用 WinForms(例如,Windows 服务两者都不使用)。检查 .exe 标头是唯一的方法。
      猜你喜欢
      • 1970-01-01
      • 2010-09-18
      • 1970-01-01
      • 2020-03-07
      • 1970-01-01
      • 1970-01-01
      • 2016-12-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多