判断程序集是否是一个gui应用程序

Determine whether assembly is a gui application

我正在尝试确定 C# 程序集是 GUI 还是控制台应用程序,以便构建一个可以自动重新创建丢失的快捷方式的工具。

目前,我有一个例程递归地步进 Program Files 中的所有目录(和 x86 目录)。

对于它找到的每个 EXE,该工具调用 IsGuiApplication,传递 EXE 的名称。

从那里,我使用 LoadFrom 创建了一个 Assembly 对象。 我想检查这个程序集是否有 GUI 输出,但我不确定如何在 C# 中测试它。

我目前的想法是使用 GetStdHandle,但我不确定如何将其应用于 运行 应用程序之外的程序集。

我在 C# 中的反射经验有限,因此我们将不胜感激。

using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace BatchShortcutBuild
{
    class Program
    {
        //I'm uncertain that I need to use this method
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern IntPtr GetStdHandle(int nStdHandle);

        static void Main(string[] args) {
            BuildShortcuts();
            Console.ReadLine();
        }

        public static void BuildShortcuts() {
            String dirRoot = "C:\Program Files\";
            processRoot(dirRoot);

            dirRoot = "C:\Program Files (x86)\";
            processRoot(dirRoot);

            Console.WriteLine("Finished enumerating files");
            Console.ReadLine();
        }


        public static void processRoot(String path) {
            try {
                foreach (String theDir in Directory.EnumerateDirectories(path)) {
                    processRoot(theDir);
                }

                foreach (String theFile in Directory.EnumerateFiles(path, "*.exe")) {
                    if (IsGuiApplication(theFile)) {
                        //I would generate a shortcut here
                    }
                }
            } catch { }
        }

        public static bool IsGuiApplication(String filePath) {
            Console.WriteLine(filePath);
            Assembly a = Assembly.LoadFrom(filePath);
            //How to get the program type from the assembly?
            return false;
        }

    }
}

使用GetReferencedAssemblies()获取所有引用的程序集并查找system.windows.forms程序集

    AssemblyName[] referencedAssemblies = assm.GetReferencedAssemblies();
    foreach (var assmName in referencedAssemblies)
    {
      if (assmName.Name.StartsWith("System.Windows"))
       //bingo
    }

检测 GUI 应用程序的基本思路是 GUI 应用程序始终使用程序集 System.Windows.*

bool isGui(Assembly exeAsm) {
   foreach (var asm in exeAsm.GetReferencedAssemblies()) {
       if (asm.FullName.Contains("System.Windows"))
          return true;
   }
   return false;
}

这将检测所有 windows 形式的 .NET 应用程序,甚至是 WPF

为了安全起见,@Killany 和@Nissim 建议的方法并非 100% 准确,因为控制台应用程序可以引用 System.Windows.* dll(错误或需要'System.Windows' 程序集提供的其他功能)。

我不确定是否存在 100% 的方法,因为可以为某些应用程序提供参数 运行 with/without ui(即静默)

您可以检查的一件事是文件 PE header 的 .subsystem。如果您在 ILDASM 中打开文件并检查清单,如果它使用 Windows GUI 子系统,您将看到:

我认为 Assembly class 中没有任何方法可以检查这个,所以您可能需要检查文件本身。

另一种检查方法是检查程序集中的类型,看看它们是否派生自 System.Windows.Forms.Form(Windows 表单)或 System.Windows.Window(WPF):

private static bool HasGui(Assembly a)
{
    return a.DefinedTypes
        .Any(t => typeof(System.Windows.Forms.Form).IsAssignableFrom(t) ||
                  typeof(System.Windows.Window).IsAssignableFrom(t));
}

请注意,您需要添加对 System.Windows.Forms.dllPresentationFramework.dll 的引用才能访问这些类型。

您可以使用 Assembly.LoadFrom(string) 加载程序集。我自己测试了这个方法,它看起来有点慢,所以也许你可以通过涉及 Parallel.ForEach.

来让它更快

之前多次提到,您可以阅读子系统字段。

    private PEFileKinds GetFileType(string inFilename)
    {
        using (var fs = new FileStream(inFilename, FileMode.Open, FileAccess.Read))
        {
            var buffer = new byte[4];
            fs.Seek(0x3C, SeekOrigin.Begin);
            fs.Read(buffer, 0, 4);
            var peoffset = BitConverter.ToUInt32(buffer, 0);
            fs.Seek(peoffset + 0x5C, SeekOrigin.Begin);
            fs.Read(buffer, 0, 1);
            if (buffer[0] == 3)
            {
                return PEFileKinds.ConsoleApplication;
            }
            else if (buffer[0] == 2)
            {
                return PEFileKinds.WindowApplication;
            }
            else
            {
                return PEFileKinds.Dll;
            }
        }
    }