Determine whether assembly is a gui application

2019-07-09 02:16发布

I am trying to determine whether a C# assembly is a GUI or a Console application in order to build a tool which will automatically recreate lost short cuts.

Currently, I have a routine which recursively steps all directories in Program Files (and the x86 directory).

For each EXE it finds, the tool calls IsGuiApplication, passing the name of the EXE.

From there, I create an Assembly object using LoadFrom. I want to check whether this assembly is has a GUI output, but I'm unsure how to test this in C#.

My current idea is to use GetStdHandle, but I'm not sure how to apply this to an assembly outside of the running application.

My experience with reflection in C# is limited, so any help would be appreciated.

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;
        }

    }
}

5条回答
叼着烟拽天下
2楼-- · 2019-07-09 02:49

Just to be safe here, the method suggested by @Killany and @Nissim suggest is not 100% accurate, as console applications can reference the System.Windows.* dlls (either by mistake or by a need of other functionality given by the 'System.Windows' assembly).

I'm not sure a 100% method exist, as some applications can be given a parameter to run with/without ui (i.e. silently)

查看更多
smile是对你的礼貌
3楼-- · 2019-07-09 02:50

A basic idea to detect GUI apps is that GUI apps always use assembly System.Windows.*.

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

This will detect all .NET applications that are windows forms, or even WPF

查看更多
唯我独甜
4楼-- · 2019-07-09 03:06

Use GetReferencedAssemblies() to get all referenced assemblies and look for the system.windows.forms assembly

    AssemblyName[] referencedAssemblies = assm.GetReferencedAssemblies();
    foreach (var assmName in referencedAssemblies)
    {
      if (assmName.Name.StartsWith("System.Windows"))
       //bingo
    }
查看更多
爷、活的狠高调
5楼-- · 2019-07-09 03:09

As several times mentioned before, you can read the Subsystem Field.

    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;
            }
        }
    }
查看更多
老娘就宠你
6楼-- · 2019-07-09 03:10

One thing you could check is the .subsystem of the file's PE header. If you open up the file in ILDASM and check the manifest, you'll see this if it uses the Windows GUI subsystem:

enter image description here

I don't think there's any method in the Assembly class to check this, so you'll probably need to check the file itself.

Another way to check would be to go through the types in the assembly and see if any of them derive from System.Windows.Forms.Form (Windows Forms) or 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));
}

Note that you'll need to add references to System.Windows.Forms.dll and PresentationFramework.dll to gain access to these types.

You can use Assembly.LoadFrom(string) to load the assembly. I tested this method myself and it seemed a bit slow so perhaps you can make it faster by involving Parallel.ForEach.

查看更多
登录 后发表回答