Determine whether assembly is a gui application - c#

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 (i.e. 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.

Related

Autocad & System.Addin, FileNotFoundException for Autocads basic dlls

i just started to develop applications for AutoCAD 2016. I want to load my dLLs into a separate AppDomain, so that i don't have to restart ACAD all the time.
After a lot of research and trying i ended up with a pipeline solution
using System.Addin and System.Addin.Contract.
I use only interfaces and standardclasses for the Views Contract and Adapters like in this example here.
This is my addin containing one methode to write Hello into Acad's Editor and a second methode for drawing a line.
using System.AddIn;
using CADAddinView;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
namespace CADAddIn
{
[AddIn("cadAddIn", Version = "1.0.0.0")]
public class CADAddIn : ICADAddinView
{
public void drawLine()
{
Document acDoc = Autodesk.AutoCAD.ApplicationServices.Core.Application.DocumentManager.MdiActiveDocument;
Database acCurDb = acDoc.Database;
using (DocumentLock acLckDoc = acDoc.LockDocument())
{
using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
{
DBObject blkTbl = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead);
BlockTable acBlkTbl = blkTbl as BlockTable;
BlockTableRecord acBlkTblRec = (BlockTableRecord)acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
Polyline acPoly = new Polyline();
acPoly.SetDatabaseDefaults();
acPoly.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0);
acPoly.AddVertexAt(0, new Point2d(100, 100), 0, 0, 0);
acBlkTblRec.AppendEntity(acPoly);
acTrans.AddNewlyCreatedDBObject(acPoly, true);
acTrans.Commit();
}
}
}
public void sayHello()
{
Editor ed = Autodesk.AutoCAD.ApplicationServices.Core.Application.DocumentManager.MdiActiveDocument.Editor;
ed.WriteMessage("Hello");
}
}
}
this is my HostApplication:
using System.AddIn.Hosting;
using System.Windows.Forms;
using CADHostView;
using System;
using System.Collections.ObjectModel;
using Autodesk.AutoCAD.Runtime;
namespace CADHost
{
public class CADHost
{
[CommandMethod("sayHello")]
public static void sayHello()
{
string addInPath = Environment.CurrentDirectory + "\\Pipeline";
string[] warnings = AddInStore.Update(addInPath);
foreach (string warning in warnings)
{
MessageBox.Show(warning);
}
Collection<AddInToken> tokens = AddInStore.FindAddIns(typeof(ICADHostView), addInPath);
if (tokens.Count == 0)
{
MessageBox.Show("No AddIn found.");
}
else
{
AddInToken cadToken = tokens[0];
ICADHostView cadApp = cadToken.Activate<ICADHostView>(AddInSecurityLevel.Host);
cadApp.sayHello();
}
}
[CommandMethod("drawLine")]
public static void drawLine()
{
string addInPath = Environment.CurrentDirectory + "\\Pipeline";
string[] warnings = AddInStore.Update(addInPath);
foreach (string warning in warnings)
{
MessageBox.Show(warning);
}
Collection<AddInToken> tokens = AddInStore.FindAddIns(typeof(ICADHostView), addInPath);
if (tokens.Count == 0)
{
MessageBox.Show("No AddIn found.");
}
else
{
AddInToken cadToken = tokens[0];
ICADHostView cadApp = cadToken.Activate<ICADHostView>(AddInSecurityLevel.Host);
cadApp.drawLine();
}
}
}
}
Both of the two applications reference to three standard-Dlls from Acad:
accoremgd.dll, acdbmgd.dll, acmgd.dll.
In both projects these dlls have the option local copy false.
If i start then i get an Exception, where the programm cannot find the file "accoremgd.dll" and Acad crashes.
So i tried to set the Option local copy true only for the Addin.
Now it works for the "sayHello"-Methode.
but i get an invalide cast exception when acBlkTbl is initialised.
Would be great if someone has the last steps for me to make this work.
Also great would be a working example must not be made with the Addinsystem
i only want to make this work for not restarting acad all the time^^
Thank you for your help
matthias
I don't believe a separate AppDomain will work, when you call AutoCAD object types it will go to the main AppDomain and get messed up...
As just want to edit your code and don't restart, you'll be better with Edit & Continue feature (available since VC2013 on AutoCAD 2015, I believe).
This is not supported. AutoCAD is a very old and complex program and most of the AutoCAD API objects cannot be used in remote fashion.
Please read:
http://through-the-interface.typepad.com/through_the_interface/2008/09/tired-of-not-be.html
http://forums.autodesk.com/t5/net/netload-is-there-a-net-unload-command/td-p/2404002
https://www.theswamp.org/index.php?topic=38675.0
In the #3, you can see that the AutoCAD development team confirmed that there are some global variables which will prevent working this way.
I gave up my tries to solve this problem. My current "best" solution is to load dlls at the start of AutoCAD. At least i don't have to netload every dll.
If someone has a better solution feel free to tell me^^ Thanks to all that answered. matthias

Loading dll library from Resource to Current Domain(Embedding dll in main exe file)

I'm trying to load dll libraries during runtime using the following code so that I don't have to provide the user with lot of dll files along with the main executable file. I have inlude all the dll files as an embedded resource and also in the reference part I have include them and have set the CopyLocal property to false. But the problems here are:1. All the dll are getting copied to Bin\Debug folder2. I'm getting FileNotFoundException.I did lot of searches to get these things resolved and finally I'm here. I got a similar code here but still couldn't do anything. What should I do to prevent this exception...??
Is there a better way to do the same thing for a Windows Form Application(Not WPF)...??
using System;
using System.Linq;
using System.Windows.Forms;
using System.Diagnostics;
using System.Reflection;
using System.Collections.Generic;
using System.IO;
namespace MyNameSpace
{
static class Program
{
static int cnt;
static IDictionary<string, Assembly> assemblyDictionary;
[STAThread]
static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
if (cnt != 1)
{
cnt = 1;
Assembly executingAssembly = Assembly.GetExecutingAssembly();
string[] resources = executingAssembly.GetManifestResourceNames();
foreach (string resource in resources)
{
if (resource.EndsWith(".dll"))
{
using (Stream stream = executingAssembly.GetManifestResourceStream(resource))
{
if (stream == null)
continue;
byte[] assemblyRawBytes = new byte[stream.Length];
stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
try
{
assemblyDictionary.Add(resource, Assembly.Load(assemblyRawBytes));
}
catch (Exception ex)
{
MessageBox.Show("Failed to load: " + resource + " Exception: " + ex.Message);
}
}
}
}
Program.Main();
}
if (cnt == 1)
{
cnt = 2;
System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Highest;
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
Application.ApplicationExit += new EventHandler(Application_ApplicationExit);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
{
AssemblyName assemblyName = new AssemblyName(args.Name);
string path = assemblyName.Name + ".dll";
if (assemblyDictionary.ContainsKey(path))
{
return assemblyDictionary[path];
}
return null;
}
}
}
If I'm using something unnecessarily in my code then you can show me the right way...
I'm a student working on Windows Form Application v4.0 project for my papers to be submitted.
If it is still the case that you must do this, then use this OnResolveAssembly method. There is no need to preload them into an array if you don't want to. This will load them the first time they are actually needed.
Then just:
add the some.assembly.dll file to the project.
probably not a reference to the project's output
but the file that is the result of the DLL project.
mark it as a Resource in the file properties.
// This function is not called if the Assembly is already previously loaded into memory.
// This function is not called if the Assembly is already in the same folder as the app.
//
private static Assembly OnResolveAssembly(object sender, ResolveEventArgs e)
{
var thisAssembly = Assembly.GetExecutingAssembly();
// Get the Name of the AssemblyFile
var assemblyName = new AssemblyName(e.Name);
var dllName = assemblyName.Name + ".dll";
// Load from Embedded Resources
var resources = thisAssembly.GetManifestResourceNames().Where(s => s.EndsWith(dllName));
if (resources.Any())
{
// 99% of cases will only have one matching item, but if you don't,
// you will have to change the logic to handle those cases.
var resourceName = resources.First();
using (var stream = thisAssembly.GetManifestResourceStream(resourceName))
{
if (stream == null) return null;
var block = new byte[stream.Length];
// Safely try to load the assembly.
try
{
stream.Read(block, 0, block.Length);
return Assembly.Load(block);
}
catch (IOException)
{
return null;
}
catch (BadImageFormatException)
{
return null;
}
}
}
// in the case the resource doesn't exist, return null.
return null;
}
-Jesse
PS: This comes from http://www.paulrohde.com/merging-a-wpf-application-into-a-single-exe/
Try the following:
For each .dll resource:
If the file allready exists on the AppDomain.Current.BaseDirectory then continue to the next resource
Else save the resource to the AppDomain.Current.BaseDirectory. Do this in a try-catch and if it fails, notify the user. For this step to complete successfully you will need write access on the installation folder (usually a subfolder of "Program Files"). This will be solved by running the program as an administrator the first time only ao that the files are written on the file system.
Ιf the assemblies are referenced by your VS project then you do not have to load them yourself. To understand why this work's you will need to understand how assemblies are located by the CLR.
Else you will need to load each assembly yourself using one of the Assembly.Load that take either a string or and AssemblyName as a parameter.

Getting DLLs in WinForms application

I have to create a class that will load all the dll's from repository and check whether
they are inheriting from IMFServicePlugin interface and returns the
valid dlls.
that I have done using this...
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Windows.Forms.ComponentModel;
using MFDBAnalyser;
namespace MFDBAnalyserAssemblyValidator
{
public class MFDBAnalyserAssemblyValidator
{
static void Main(string[] args)
{
List<string> assemblyNames = new List<string>();
Assembly[] oAssemblies = new Assembly[args.Length];
for (int assemblyCount = 0; assemblyCount < args.Length; assemblyCount++)
{
oAssemblies[assemblyCount] = Assembly.LoadFile(args[assemblyCount]);
try
{
foreach (Type oType in oAssemblies[assemblyCount].GetTypes())
{
// Check whether class is inheriting from IMFServicePlugin.
if (oType.GetInterface("IMFDBAnalyserPlugin") == typeof(IMFDBAnalyserPlugin))
{
assemblyNames.Add(args[assemblyCount].Substring(args[assemblyCount].LastIndexOf("\\") + 1));
}
}
}
catch (Exception ex)
{
lblError.Text = "ERROR";
}
}
// Passing data one application domain to another.
AppDomain.CurrentDomain.SetData("AssemblyNames", assemblyNames.ToArray());
}
}
}
but this was for loading the dll from the repository but I also want to store these dll in another ORM class.
Can anybody help me out...
If possible plz provide some links so that I can get a sufficient idea of how dll works for an windows/desktop application.
At a first tip you should use Assembly.ReflectionOnlyLoad(). Cause if you load the assembly by using Assembly.LoadFile() the assembly will automatically be put into your local AppDomain!

Referencing files

I am making an application which is almost done but there is one thing that is bugging me. There are about 12-13 files that must be in the directory of the folder (some .dlls, some .xml files etc.) for the application to run, and I want to make my application as compact as possible, meaning I want as fewer files to go with the application. So my question is, how can I do this? Can all the files be included in the application itself? Is it necessary for the .dlls to be in the application folder or can I reference them from somewhere else? I was thinking to make a folder for all those files but I don't think my application will run if a .dll file isn't placed in the same directory as the application.
You can handle the AppDomain.AssemblyResolve event and call Assembly.Load(path) to load DLLs from non-standard folders.
You can even call Assembly.Load(byte[]) to load a DLL that is embedded in your EXE as a resource.
Note that the JITter will load all types used by a method before the method starts executing (in order to compile the method).
Therefore, you must add the event handler before using any methods or types in the DLLs, and the method that adds the handler cannot directly use the DLLs.
How to embed and access resources by using Visual C# looks like just what you need.
[edit]
If you want to load DLLs, you can combine the above with the AppDomain.AssemblyResolve event SLaks mentions like this:
using System.IO;
using System.Reflection;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve +=
new ResolveEventHandler(MyResolveEventHandler);
var myWrappedClass1 =
currentDomain.CreateInstance(
"ConsoleApplication3.ClassLibrary1.dll",
"ClassLibrary1.Class1");
var myClass1 = myWrappedClass1.Unwrap();
Console.WriteLine(myClass1.GetType().InvokeMember(
"Add",
BindingFlags.Default | BindingFlags.InvokeMethod,
null,
myClass1,
new object[] { 1, 1 }));
Console.ReadLine();
}
private static Assembly MyResolveEventHandler(
object sender, ResolveEventArgs args)
{
Assembly currentAssembly=null;
Stream dllStream;
try
{
currentAssembly = Assembly.GetExecutingAssembly();
dllStream =
currentAssembly.GetManifestResourceStream(args.Name);
var length = (int)dllStream.Length;
var dllByteArray = new byte[length];
int bytesRead;
int offset = 0;
while ((bytesRead = dllStream.Read(
dllByteArray,
offset,
dllByteArray.Length - offset))
> 0)
offset += bytesRead;
return Assembly.Load(dllByteArray);
}
catch
{
Console.WriteLine("Error accessing resources!");
}
return null;
}
}
}
where Class1 is a class library containing just:
namespace ClassLibrary1
{
public class Class1
{
public int Add(int x, int y)
{
return x + y;
}
}
}
and the DLL is added as an Embedded Resource to the file:

How do I restore a file from the recycle bin using C#?

Moving files to the recycle bin and emptying the recycle bin are well documented, but how can a file be programmatically restored from the recycle bin?
There seems not to be a solution in pure C#. You most likely have to resort to P/Invoke.
This article presents a solution in C++ using the SHFileOperation API.
The only other reference to this beyond the previously mentioned link to codeproject that I can see mentions this:
Call SHGetFolderLocation passing CSIDL_BITBUCKET.
Then you can manipulate that folder as usual.
You'll have to create an interop for the SHGetFolderLocation function.
CSIDL_BITBUCKET being the CSIDL ("constant special item ID list") value for the virtual Recycle Bin folder. The quote is taken from here, and will involve interop with the Windows shell. MSDN also mentions that this function has been deprecated in favour of another in Vista.
Hope below code will work to restore the files. Please make sure, STA Calls only supported for shell calls
using System;
using System.Collections;
using System.Windows.Forms;
using System.IO;
using Shell32; //Reference Microsoft Shell Controls And Automation on the COM tab.
using System.Runtime.InteropServices;
using Microsoft.VisualBasic.FileIO;
using System.Threading;
private static void Restore(object param)
{
object[] args = (object[])param;
string filename = (string)args[0];
string filepath = (string)args[1];
Shl = new Shell();
Folder Recycler = Shl.NameSpace(10);
var c = Recycler.Items().Count;
var _recycler = Recycler.Items();
for (int i = 0; i < _recycler.Count; i++)
{
FolderItem FI = _recycler.Item(i);
string FileName = Recycler.GetDetailsOf(FI, 0);
if (Path.GetExtension(FileName) == "") FileName += Path.GetExtension(FI.Path);
//Necessary for systems with hidden file extensions.
string FilePath = Recycler.GetDetailsOf(FI, 1);
if (filepath == Path.Combine(FilePath, FileName))
{
DoVerb(FI, "ESTORE");
break;
}
}
}
private static bool DoVerb(FolderItem Item, string Verb)
{
foreach (FolderItemVerb FIVerb in Item.Verbs())
{
if (FIVerb.Name.ToUpper().Contains(Verb.ToUpper()))
{
FIVerb.DoIt();
return true;
}
}
return false;
}

Categories

Resources