Hello,
I'm writing a WMI Provider in C#. For this, the way to install it manually is to record it in the GAC and call installutil to have the C# WMI Provider Installer creating the MOF File and compile it.
I've looked at the GAC example and I manage to register the DLL in the GAC.
However when I try to a an ElevatedManagedAction to call Tasks.InstallServic to run InstallUtil it fails.
I'm therefore not sure at which stage of the WiX process the DLL is considered to be registered in the GAC and what parameter should I pass to Tasks.InstallService as a step.
The default comes with InstallFiles, but I'm not sure for that compared to InstallService or other?
Is there an example with GAC then DA somewhere?
What you can do is to us an AfterInstall event (it is already elevated) and spawn the GAC registration process:
```C#
project.Before install += e =>
{
if (e.IsUninstalling)
Process.Start("gacutil", $"-u \"{e.InstallDir.PathJoin("my_asm.dll")}\"");
};
project.AfterInstall += e =>
{
if (e.IsInstalling)
Process.Start("gacutil", $"-i \"{e.InstallDir.PathJoin("my_asm.dll")}\"");
};
```
Just don't forget to add the error handling to the pseudo-code above
AfterInstall seems elevated but not BeforeInstall, am I right?
Correct.
The elevation may have an undesirable side effects so it's not administered unless it's truly needed.
I have just added extension methods to allow an easy elevated execution. But you do not need o waith for the release. You can just add ProcessExtensions class as below and then use in tn both event handlers:
```C#
public static class ProcessExtensions
{
public static Process StartElevated(this string fileName, string args = "")
{
bool isAdmin = new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
return Process.Start(new ProcessStartInfo
{
WorkingDirectory = Path.GetDirectoryName(Path.GetFullPath(fileName)),
FileName = fileName,
Arguments = args,
UseShellExecute = true,
Verb = isAdmin ? "" : "runas"
});
}
}
. . .
project.Before install += e =>
{
if (e.IsUninstalling)
"gacutil".StartElevated($"-u \"{e.InstallDir.PathJoin("my_asm.dll")}\"");
};
project.AfterInstall += e =>
{
if (e.IsInstalling)
"gacutil".StartElevated($"-i \"{e.InstallDir.PathJoin("my_asm.dll")}\"");
};
```
Thanks
I'll try this for installutil in https://github.com/1Dimitri/InventoryWMIProvider/blob/master/ProviderInstaller/Program.cs .
However I didn't find any proper solution to elevate non-external binaries, aka parts of the .NET Code in BeforeInstall when using 3rd party libraries(Example in https://github.com/1Dimitri/VSCELicense/blob/master/VSCELicInstallers/VSCELicense_WixSharpInstaller/Program.cs ).
You can add an ElevartedAction but then you will need to ensure that the action is scheduled properly. Not always easy but doable.
Though there is another not so obvious approach. The assembly that your event handler is implemented in is also an executable. So there is no dependency on any external executable:
```C#
static int Main(string[] args)
{
if (args.FirstOrDefault() == "elevated_before_install_action")
{
try
{
bool isUninstall = args.Contains("-u");
string installDir = args[1];
ElevatedBeforeInstall(installDir, isUninstall);
}
catch(Exception e)
{
return 1; // error
}
return 0;
}
var project = new ManagedProject(. . .
. . .
project.Before install += e =>
{
System.Reflection.Assembly
.GetExecutingAssembly()
.Location
.StartElevated($"elevated_before_install_action \"{e.InstallDir}\" {e.IsUninstalling? "-u"}");
};
. . .
}
static void ElevatedBeforeInstall(string installDir, bool isUninstall)
{
. . .
}
```
I made some tests about GAC & InstallUtil order
new Dir(@"%ProgramFiles%\Whatever",
new Assembly(@"MyAssembly.dll", true,
new NativeImage { Platform = NativeImagePlatform.all, Priority = 1 }))
},
System.Reflection.Assembly asm = System.Reflection.Assembly.LoadFrom(@"MyAssembly.dll");
string strongname = asm.GetName().FullName;
and then passed it as a property:
project.Properties = new Property[]
{
new Property("StrongName",strongname)
}
The actions can be carried out as follows:
Actions = new[] {
new ElevatedManagedAction(CustomActions.InstallService, Return.check, When.After, Step.MsiPublishAssemblies, Condition.NOT_Installed) {
UsesProperties ="StrongName"
},
new ElevatedManagedAction(CustomActions.UnInstallService, Return.check, When.Before, Step.MsiUnpublishAssemblies, Condition.BeingUninstalled)
{
UsesProperties = "StrongName"
}
}
CustomActions can be defined as:
public class CustomActions
{
[CustomAction]
public static ActionResult InstallService(Session session)
{
return session.HandleErrors(() =>
{
ProcessExtensions.StartInstallUtilElevated(session.Property("StrongName"), true, "/AssemblyName");
});
}
[CustomAction]
public static ActionResult UnInstallService(Session session)
{
return session.HandleErrors(() =>
{
ProcessExtensions.StartInstallUtilElevated(session.Property("StrongName"),false,"/AssemblyName");
});
}
}
With the ProcessExtensions you publish above used to create StartInstallUtilElevated re-using also the InstallService code from the CommonTasks:
public static Process StartInstallUtilElevated(string assemblyFilePathOrName, bool isInstalling, string args = null)
{
string InstallUtilExePath = System.IO.Path.Combine(Tasks.CurrentFrameworkDirectory, "InstallUtil.exe");
string InstallUtilArguments = string.Format("{0} {1} \"{2}\"", isInstalling ? "" : "/u", args ?? "", assemblyFilePathOrName);
return StartElevated(InstallUtilExePath, InstallUtilArguments);
}
Great.
An excellent solution.
I have also put an extra comment to the sample referencing your solution:

For reference, I've created a tag in my WMIProvider project where is (almost) nothing as far as the project goal is concerned.
It just creates a one class WMI Provider returning the version the assembly beneath.
But it includes the complete WixSharp code in a dedicated project ("ProviderInstaller")
https://github.com/1Dimitri/InventoryWMIProvider/releases/tag/1.0.0.1