check if WMI namespace exists from c# - c#

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

Related

WMI: Register Assembly with WMI Repository via InstrumentationManager.RegisterAssembly

I am trying to write a WMI provider in C# (version 3.5). It'll be a dll that I can install into the GAC and it communicates with a running service via a pipe. Due to a custom updater mechanism on the service, I would like to programatically register and unregister the provider with the WMI repository instead of using installutil.exe.
To isolate my issues and solve them one at a time, I've started with the sample provider from the following link: https://msdn.microsoft.com/en-us/library/cc268228.aspx. I can build it as a DLL, insert it into the GAC (gacutil.exe), and Register it with the WMI repository via InstallUtil.exe. Queries to it via powershell work correctly and I can see its classes under its namespace when I use wbemtest.exe.
However, I've been unsuccessful in my attempts to register the assembly with the WMI repository when I use a separate app I wrote for using InstrumentationManager.RegisterAssembly.
Below is my attempt to programatically install the DLL based on the above sample. I'd appreciate any guidance or directions to samples that show how to implement a provider dll in this manner.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;
using System.Management.Instrumentation;
using System.Configuration.Install;
//using System.EnterpriseServices.Internal;
namespace wmi_register_tool
{
class Program
{
static void Main(string[] args)
{
bool installing = true;
string asmPath = "";
if (!ParseInput(args, ref installing, ref asmPath))
{
PrintUsage();
return;
}
Assembly myAssem = Assembly.Load(File.ReadAllBytes(asmPath));
Console.WriteLine(myAssem.FullName);
Console.WriteLine("Types contained in " + asmPath + " assembly");
foreach (Type oType in myAssem.GetTypes())
{
Console.WriteLine("\t" + oType.Name);
}
//Publish p = new Publish();
//p.RegisterAssembly(myAssem);
AssemblyInstaller wmi_installer = new AssemblyInstaller(myAssem, null);
Console.WriteLine("--------------> Starting AssemblyInstaller.Install()");
wmi_installer.Install(null);
Console.WriteLine("--------------> Done with AssemblyInstaller.Install()");
Console.WriteLine("--------------> Starting AssemblyInstaller.Commit()");
wmi_installer.Commit(null);
Console.WriteLine("--------------> Done with AssemblyInstaller.Commit()");
try
{
Console.WriteLine("--------------> Starting Instrumentation.RegisterAssembly()");
//Instrumentation.RegisterAssembly(myAssem);
InstrumentationManager.RegisterAssembly(myAssem);
Console.WriteLine("--------------> Done with Instrumentation.RegisterAssembly()");
}
catch (Exception e)
{
Console.WriteLine("Exception Caught: {0}", e);
Console.WriteLine("Exception Message: " + e.Message);
}
}
static bool ParseInput(string[] args, ref bool installing, ref string asmPath)
{
if (args.Length < 2 || args[0].Equals("/h") || args[0].Equals("-h"))
{
return false;
}
asmPath = args[1];
if (args[0].Equals("-i"))
{
installing = true;
return true;
}
else if(args.Equals("-u"))
{
installing = false;
return true;
}
return false;
}
static void PrintUsage()
{
System.Console.WriteLine("wmi_register_tool.exe \n\t-i <filename>\n\t-u <filename>");
}
}
}
I get the following output where it appears to succeed, but then I don't find anything in the WMI repository, so RegisterAssembly must not have successfully inserted it.
C:\wmi_testing>"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\gacutil.exe" /i WmiServic
eHost.dll
Microsoft (R) .NET Global Assembly Cache Utility. Version 3.5.30729.1
Copyright (c) Microsoft Corporation. All rights reserved.
Assembly successfully added to the cache
C:\wmi_testing>wmi_register_tool.exe -i WmiServiceHost.dll
WmiServiceHost, Version=1.0.0.3, Culture=neutral, PublicKeyToken=3cd934646523c30c
Types contained in WmiServiceHost.dll assembly
SERVICE_STATUS_PROCESS
SCM_ACCESS
SERVICE_ACCESS
SC_STATUS_TYPE
ServiceHandle
SCM
MyInstall
Win32_Process
WIN32ServiceHost
<EnumerateServiceHosts>d__0
--------------> Starting AssemblyInstaller.Install()
Installing assembly 'C:\Windows\assembly\GAC_MSIL\WmiServiceHost\1.0.0.3__3cd934646523c30c\WmiServiceHost.dll'.
Affected parameters are:
assemblypath = C:\Windows\assembly\GAC_MSIL\WmiServiceHost\1.0.0.3__3cd934646523c30c\WmiServiceHost.dll
logfile = C:\Windows\assembly\GAC_MSIL\WmiServiceHost\1.0.0.3__3cd934646523c30c\WmiServiceHost.InstallLog
Installing WMI Schema: Started
Registering assembly: WmiServiceHost_SN_3cd934646523c30c_Version_1.0.0.3
Ensuring that namespace exists: root\MicrosoftWmiNet
Ensuring that class exists: root\MicrosoftWmiNet:WMINET_Instrumentation
Ensuring that class exists: CREATING root\MicrosoftWmiNet:WMINET_Instrumentation
Ensuring that class exists: root\MicrosoftWmiNet:WMINET_InstrumentedNamespaces
Ensuring that class exists: CREATING root\MicrosoftWmiNet:WMINET_InstrumentedNamespaces
Ensuring that class exists: root\MicrosoftWmiNet:WMINET_Naming
Ensuring that class exists: CREATING root\MicrosoftWmiNet:WMINET_Naming
Ensuring that namespace exists: root\default
Ensuring that class exists: root\default:WMINET_Instrumentation
Ensuring that class exists: CREATING root\default:WMINET_Instrumentation
Ensuring that class exists: root\default:WMINET_InstrumentedAssembly
Ensuring that class exists: CREATING root\default:WMINET_InstrumentedAssembly
Ensuring that class exists: root\default:MSFT_DecoupledProvider
Ensuring that class exists: CREATING root\default:MSFT_DecoupledProvider
Ensuring that class exists: root\default:WMINET_ManagedAssemblyProvider
Ensuring that class exists: CREATING root\default:WMINET_ManagedAssemblyProvider
Installing WMI Schema: Finished
--------------> Done with AssemblyInstaller.Install()
--------------> Starting AssemblyInstaller.Commit()
See the contents of the log file for the C:\Windows\assembly\GAC_MSIL\WmiServiceHost\1.0.0.3__3cd934646523c30c\WmiServic
eHost.dll assembly's progress.
The file is located at C:\Windows\assembly\GAC_MSIL\WmiServiceHost\1.0.0.3__3cd934646523c30c\WmiServiceHost.InstallLog.
Committing assembly 'C:\Windows\assembly\GAC_MSIL\WmiServiceHost\1.0.0.3__3cd934646523c30c\WmiServiceHost.dll'.
Affected parameters are:
assemblypath = C:\Windows\assembly\GAC_MSIL\WmiServiceHost\1.0.0.3__3cd934646523c30c\WmiServiceHost.dll
logfile = C:\Windows\assembly\GAC_MSIL\WmiServiceHost\1.0.0.3__3cd934646523c30c\WmiServiceHost.InstallLog
logtoconsole =
--------------> Done with AssemblyInstaller.Commit()
--------------> Starting Instrumentation.RegisterAssembly()
--------------> Done with Instrumentation.RegisterAssembly()
C:\wmi_testing>
EDIT: I tried replacing Instrumentation.RegisterAssembly with the following code and again it ran to completion with no exception, but it printed out more more information to the console indicating that things were going smoothly. In the end the results were the same and I couldn't find my namespace in the WMI repository using wbemtest.exe.
string[] installArgs = new String[] {
"//logfile=",
"//LogToConsole=false",
"//ShowCallStack",
myAssem.Location,
};
System.Configuration.Install.
ManagedInstallerClass.InstallHelper(installArgs);
In my attempt to get the RegisterAssembly function to do the work, I missed the fact that I had achieved my goal while searching for potential dependencies. The AssemblyInstaller.Install and AssemblyInstaller.Commit functions were successfully inserting my provider into the WMI repository. I'm not sure whether RegisterAssembly was removing it and/or messing up the entry or if I had just messed up my WMI repository through continual testing, but when I reset my test VM to a clean state and ran the code with some new lines that cause an exception after AssemblyInstaller.Commit and before RegisterAssembly, I noticed that my provider was correctly inserted into the WMI repository and that I could query it through powershell.
Below is the main that successfully installed it into the WMI repository after I had manually placed the DLL into the GAC.
static void Main(string[] args)
{
bool installing = true;
string asmPath = "";
if (!ParseInput(args, ref installing, ref asmPath))
{
PrintUsage();
return;
}
Assembly myAssem = Assembly.Load(File.ReadAllBytes(asmPath));
Console.WriteLine(myAssem.FullName);
AssemblyInstaller wmi_installer = new AssemblyInstaller(myAssem, null);
wmi_installer.Install(null);
wmi_installer.Commit(null);
}
And just for completeness, I'll include another version that uninstalls and checks if the assembly can be installed prior to trying.
static void Main(string[] args)
{
bool installing = true;
bool installable = false;
string asmPath = "";
if (!ParseInput(args, ref installing, ref asmPath))
{
PrintUsage();
return;
}
IDictionary mySavedState = new Hashtable();
Assembly myAssem = Assembly.Load(File.ReadAllBytes(asmPath));
Console.WriteLine(myAssem.FullName);
AssemblyInstaller wmi_installer = new AssemblyInstaller(myAssem, null);
if (installing)
{
try
{
AssemblyInstaller.CheckIfInstallable(asmPath);
installable = true;
}
catch (Exception)
{
installable = false;
}
if (installable)
{
mySavedState.Clear();
try
{
wmi_installer.Install(mySavedState);
wmi_installer.Commit(mySavedState);
}
catch (Exception)
{
wmi_installer.Rollback(mySavedState);
}
}
}
else
{
try
{
wmi_installer.Uninstall(null);
}
catch (Exception e)
{
Console.WriteLine("Uninstall failed due to: " + e.Message);
}
}
}

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

Notify when new appdomain created in the process

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.

unhandled exception c# dll

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

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!

Categories

Resources