简体   繁体   中英

Determine whether assembly is a gui application

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

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 (ie silently)

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

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
    }

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

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:

在此处输入图片说明

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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM