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!
Related
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.
Consider next situation. I have injected my managed dll into the process using EasyHook. EasyHook injects dll using separate AppDomain. Now I need a way to get notifications about creation of new AppDomain in the current process.
So the question is there a way do get notifications when a new AppDomain was created in the process?
There is no event or easy way to do it, there is a COM interrupt that allows you to get a list of app domains loaded but any events etc are all hidden from us on private interfaces.
There is two ways you could do this but both require you to actively seek the information i.e. there is no event to register too.
Using Performance Counters.
Using mscoree COM interrupt.
Both there options can complement each other but it depends what level of information you need.
Using Performance Counters
CLR has numerous performance counters available but the one we care about resides in the category ".Net CLR Loading" and it is the counter called "Total Appdomains".
Using the System.Diagnostics namespace you can get the number of app domains per instance/process running in you machine.
Like the code below:
PerformanceCounter toPopulate = new PerformanceCounter(".Net CLR Loading", "Total Appdomains", "ConsoleApplication2.vshost", true);
Console.WriteLine("App domains listed = {0}", toPopulate.NextValue().ToString());
(please note the example needs the application instance name if you create your own app make sure to change this)
You can wrap this on a loop and raise an even for your app when the number changes.
(Not elegant but there is no way around it at the moment)
Using mscoree COM interrupt
Further more if you want to List all the app domains in a process you need to make use the MSCOREE.TBL library which is a COM library used by the CLRHost.
You can find the library at C:\WINDOWS\Microsoft.NET\Framework\vXXXXXX\mscoree.tlb
using mscoree;
If you are using it on window 7 or above you must make sure that the embed assembly type in the reference properties is turned off as this assembly can not be embedded like that.
See further information on this stack post: Interop type cannot be embedded
See the code below to see how you can return and list all app domains in a process (this will return the actual AppDomain instances for each app domain).
The original stack post for this can be found here: List AppDomains in Process
public static List<AppDomain> GetAppDomains()
{
List<AppDomain> _IList = new List<AppDomain>();
IntPtr enumHandle = IntPtr.Zero;
CorRuntimeHostClass host = new mscoree.CorRuntimeHostClass();
try
{
host.EnumDomains(out enumHandle);
object domain = null;
while (true)
{
host.NextDomain(enumHandle, out domain);
if (domain == null) break;
AppDomain appDomain = (AppDomain)domain;
_IList.Add(appDomain);
}
return _IList;
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
return null;
}
finally
{
host.CloseEnum(enumHandle);
Marshal.ReleaseComObject(host);
}
}
Now that you can see how many app domains exist in a process and list them let put that to the test.
Below is a fully working example using both techniques.
using System;
using System.Collections.Generic;
using System.Drawing.Printing;
using System.Linq;
using System.Printing;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Xps.Packaging;
using System.Runtime.InteropServices;
using mscoree;
using System.Diagnostics;
using System.Threading;
namespace ConsoleApplication2
{
class AppDomainWorker
{
public void DoSomeWork()
{
while (true)
{
for (int i = 0; i < 1000; i++)
{
var hello = "hello world".GetHashCode();
}
Thread.Sleep(500);
}
}
}
class Program
{
[STAThread]
static void Main(string[] args)
{
PerformanceCounter toPopulate = new PerformanceCounter(".Net CLR Loading", "Total Appdomains", "ConsoleApplication2.vshost", true);
Console.WriteLine("App domains listed = {0}", toPopulate.NextValue().ToString());
for (int i = 0; i < 10; i++)
{
AppDomain domain = AppDomain.CreateDomain("App Domain " + i);
domain.DoCallBack(() => new Thread(new AppDomainWorker().DoSomeWork).Start());
Console.WriteLine("App domains listed = {0}", toPopulate.NextValue().ToString());
}
Console.WriteLine("List all app domains");
GetAppDomains().ForEach(a => {
Console.WriteLine(a.FriendlyName);
});
Console.WriteLine("running, press any key to stop");
Console.ReadKey();
}
public static List<AppDomain> GetAppDomains()
{
List<AppDomain> _IList = new List<AppDomain>();
IntPtr enumHandle = IntPtr.Zero;
CorRuntimeHostClass host = new mscoree.CorRuntimeHostClass();
try
{
host.EnumDomains(out enumHandle);
object domain = null;
while (true)
{
host.NextDomain(enumHandle, out domain);
if (domain == null) break;
AppDomain appDomain = (AppDomain)domain;
_IList.Add(appDomain);
}
return _IList;
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
return null;
}
finally
{
host.CloseEnum(enumHandle);
Marshal.ReleaseComObject(host);
}
}
}
}
I hope this is helpful and if you need any further help let us know.
I tring to test a new dll that I've build for c#
private void button1_Click(object sender, EventArgs e)
{
String [] first = UserQuery.Get_All_Users();
//MessageBox.Show(first);
}
but I get the following error at String [] first = UserQuery.Get_All_Users();
An unhandled exception of type 'System.NullReferenceException' occurred in User_Query.dll
Additional information: Object reference not set to an instance of an object.
I been tring to figure this one out for hours but can't find any null varibles
I post my dll in case the dll is wrong
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.DirectoryServices;
namespace User_Query
{
public class UserQuery
{
public static string[] Get_All_Users()
{
string[] names = new string[10];
var path = string.Format("WinNT://{0},computer", Environment.MachineName);
using (var computerEntry = new DirectoryEntry(path))
{
var userNames = from DirectoryEntry childEntry in computerEntry.Children
where childEntry.SchemaClassName == "User"
select childEntry.Name;
byte i = 0;
foreach (var name in userNames)
{
Console.WriteLine(name);
names[i] = name;
i++;
}
return names;
}
}
}
}
There is a problem with your. path variable... since there should be \\ instead of //
The problem here turned out not to be the code but be VS2010 not loading the dll. This happen because I decided to change the program from using the dll from the debug to the release version but I did not clean the project after doing it and therefore the program was not correctly loading the dll. All that need to be done was clean the project
I'm new to code and most things work, but I can't get this code to run. Can someone help?
I tried using System.Forms but it showed as missing a namespace. When I used using System.Windows.Forms that message went away. It does not let me use both.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
StreamReader sr = new StreamReader(#"file.csv");
// for set encoding
// StreamReader sr = new StreamReader(#"file.csv", Encoding.GetEncoding(1250));
string strline = "";
String[] _values = null;
int x = 0;
while(!sr.EndOfStream)
{
strline = sr.ReadLine();
_values = strline.Split(',');
if (_values.Length >= 6 && _values[0].Trim().Length > 0)
{
MessageBox.show(_values[1]);
}
}
sr.Close();
}
}
}
There is no such namespace System.Forms, the class you were trying to use (MessageBox) is in System.Windows.Forms. By correcting your using statement, the error went away.
Remember, you must have a reference to System.Windows.Forms.dll in your console app to use this class.
You need to reference System.Windows.Forms.dll in your project. Here is a detailed instruction how to do that.
There is no such namespace as System.Forms there is only a namespace called System.Windows.Forms, wich has the MessageBox class you are talking about. To be able to use it, you need to add a reference to the System.Windows.Forms.dll to to your project (find it in the .NET Tab in the "Add Reference ..." dialog) and it will work. Also note that MessageBox.Show() requires a capital 'S'. Please see below an optimized and fully working version of your code.
using System.IO;
using System.Windows.Forms;
namespace ConsoleApplication7
{
class Program
{
static void Main(string[] args)
{
using (StreamReader sr = new StreamReader(#"file.csv"))
{
while (sr.Peek() >= 0)
{
string strline = sr.ReadLine();
string[] values = strline.Split(',');
if (values.Length >= 6 && values[0].Trim().Length > 0)
{
MessageBox.Show(values[1]);
}
}
}
}
}
}
You try use it in Console application first you should add System.Windows.Forms dll in your references (from .Net reference tab) then use it by adding it's namespace.
I'm a bit confused here. there is no namespace called System.Forms. It's always System.Windows.Forms. And the MessageBox class is defined in System.Windows.Forms
You need to manually ADD a reference to your project for System.Windows.Forms as you are on a console application and not a Windows Application. Just add the reference.
I want to check if a certain feature is installed on a certain machine.
I have a powershell code that checks this, and now I want to check this from .net code.
I can see that in the cmdlet, the code checks if there is an invalid namespace error.
When searching the web, I found the following code:
ManagementClass myClass = new ManagementClass(scope, path, getOptions);
try
{
myClass.get();
}
catch (System.Management.Exception ex)
{
if (ex.ErrorCode == ManagementStatus.InvalidNamespace)
{
return true;
}
}
...
I want to clean this code a bit, so basically I have 2 questions:
Is there another way to check for an InvalidNamespace error? (The code I've copied was later used to invoke some method within myClass, so I wonder if I can somehow achieve my goal in a more direct way)
Do I really need the parameter getOptions?
To get all the wmi namespaces, you must first connect to the root namespace and then query for all the __NAMESPACE instances, and for each instance recursively repeat this process. about the getOptions parameter which is a ObjectGetOptions class is not necessary in this case, so can be null.
Check this code to get all the wmi namespaces (you can populate a list with that info and then check if the namespace exist in the machine)
using System;
using System.Collections.Generic;
using System.Text;
using System.Management;
namespace MyConsoleApplication
{
class Program
{
static private void GetWmiNameSpaces(string root)
{
try
{
ManagementClass nsClass = new ManagementClass( new ManagementScope(root), new ManagementPath("__namespace"), null);
foreach (ManagementObject ns in nsClass.GetInstances())
{
string namespaceName = root + "\\" + ns["Name"].ToString();
Console.WriteLine(namespaceName);
//call the funcion recursively
GetWmiNameSpaces(namespaceName);
}
}
catch (ManagementException e)
{
Console.WriteLine(e.Message);
}
}
static void Main(string[] args)
{
//set the initial root to search
GetWmiNameSpaces("root");
Console.ReadKey();
}
}
}