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