3

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

    }
}
Jester
  • 56,577
  • 4
  • 81
  • 125
Alex
  • 1,643
  • 1
  • 14
  • 32
  • Well, there can be many types of _GUI_. You could check if the assembly contains classes which derive from `System.Windows.Forms.Form`, that would be a hint that it contains a Windows Forms GUI. I'm not sure how you would check if the assembly contains a WPF GUI. – cbr Jun 17 '15 at 11:28
  • 1
    You should be able to examine the `Subsystem` field in the PE header of the executable. [Here is an example](http://code.cheesydesign.com/?p=572). – Jester Jun 17 '15 at 11:43
  • @Jester, thanks, that's exactly what I was looking for. I'd just found the same page you added as an example on your advice. – Alex Jun 17 '15 at 11:47
  • Note that reflection will only work for .NET applications. Is that still good enough for you? – Luaan Jun 17 '15 at 13:00

5 Answers5

4

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)

  • This is why I was pondering the GetStdHandle option. If I could determine that the standard output is not a console application yet is has an EXE signature, it's safe to assume that it's GUI based. I think I have fairly extensive task ahead of me to get this working... – Alex Jun 17 '15 at 11:40
  • @Alex It's easy when the application is running - you can just try to send the application a windows message, and if it's a GUI application, it's going to respond. Detecting this on an application that's *not* running is the tricky part. – Luaan Jun 17 '15 at 13:01
3

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;
            }
        }
    }
thehennyy
  • 4,020
  • 1
  • 22
  • 31
  • Thanks for this great answer. Inspired by the approach, I found out that for any **.exe** or **.dll** image that is already loaded into the current `AppDomain`, it is not necessary to locate, open, or read any disk file. You can use your same parsing code and PE offsets directly on the memory-mapped base address (VA) of any loaded image file, easily obtained by calling `GetModuleHandleW(...)` with the assembly name only (no path). See my [answer here](https://stackoverflow.com/a/8711036/147511) for more details. – Glenn Slayden Apr 09 '21 at 16:03
0

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
    }
Nissim
  • 6,395
  • 5
  • 49
  • 74
0

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

  • do you know whether this will also detect winforms applications that are not .NET? I would like the tool to handle all GUI applications, including those written in C++/Pas/VB 6, which might not necessarily be .NET. I'm not sure yet how to handle non winforms applications (eg. Borland C++ builder using their own UI library). – Alex Jun 17 '15 at 11:39
  • You thus need to lookup for native windows functions if they are used by that exe (e.g. `TranslateMessage()` or `GetMessage()` which are used in message loop). I think you can read the exe as a string (e.g. using notepad) and you will find these functions listed. –  Jun 17 '15 at 11:44
0

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.

cbr
  • 12,563
  • 3
  • 38
  • 63
  • Just as a note, a console application can still have a GUI, so the `.subsystem` check is not perfect. I've seen quite a few applications that use the console for debugging output and similar stuff, while the main application is in winforms. – Luaan Jun 17 '15 at 13:03
  • @Luaan Good point. Another thing that occurred to me regarding the second the `HasGui` method is that an class doesn't need to derive from these classes to create instances of them. Hmm. I guess you'd need to check if a class creates instances of these classes to tell whether it uses a GUI or not. – cbr Jun 17 '15 at 13:32