I am creating a decoupled WMI provider in a class library. Everything I have read points towards including something along these lines:
[System.ComponentModel.RunInstaller(true)]
public class MyApplicationManagementInstaller : DefaultManagementInstaller { }
I gather the purpose of this installation is because the Windows WMI infrastructure needs to be aware of the structure of my WMI provider before it is used.
My question is - when is this "installer" ran? MSDN says that the installer will be invoked "during installation of an assembly", but I am not sure what that means or when it would happen in the context of a class library containing a WMI provider.
I was under the impression that this was an automated replacement for manually running InstallUtil.exe against the assembly containing the WMI provider, but changes I make to the provider are not recognised by the Windows WMI infrastructure unless I manually run InstallUtil from the command prompt. I can do this on my own machine during development, but if an application using the provider is deployed to other machines - what then?
It seems that this RunInstaller / DefaultManagementInstaller combination is not working properly - correct?
As I understand, DefaultManagementInstaller is ran by installutil.exe - if you don't include it, the class is not installed in WMI. Maybe it is possible to create a 'setup project' or 'installer project' that runs it, but I'm not sure because I don't use Visual Studio.
[edit]
for remote instalation, an option could be to use Installutil with /MOF option to generate MOF for the assembly and use mofcomp to move it to WMI.
I use something like this to call InstallUtil programmatically:
public static void Run( Type type )
{
// Register WMI stuff
var installArgs = new[]
{
string.Format( "//logfile={0}", #"c:\Temp\sample.InstallLog" ), "//LogToConsole=false", "//ShowCallStack",
type.Assembly.Location,
};
ManagedInstallerClass.InstallHelper( installArgs );
}
Call this from your Main() method.
-dave
Thanks Uros. It does look like all that RunInstaller and DefaultManagementInstaller do is enable you to run InstallUtil successfully against the assembly. This is strange because I'm almost certain that I didn't know about InstallUtil at the point where I'd compiled and played with my first WMI provider.
I will look in to using the MOF file and for my own use I can just run the InstallUtil command line as a post build event in VS.
Related
I'm trying to execute a command from inside a .NET process. The command works just fine in Powershell or a regular command prompt executed by the same user as used for the .NET process.
I am trying to update the RRAS SSTP cert on Windows server 2016 with the following command:
netsh ras set sstp-ssl-cert name=bla.domain.foo
The error I'm getting is the following:
The supplied kernel information version is invalid.
This is the code I'm using to execute from inside a simple .NET console application. Outputting logic omitted for brevity.
using (var powerShellInstance = PowerShell.Create())
{
powerShellInstance.AddScript(command);
powerShellInstance.Invoke();
}
I'm using the .NET framework 4.6.1, I have tried adding a manifest to up the required execution rights but neither that or running as admin manually changes anything.
The application is executed by a scheduled task and if I manually add the required script as a step directly executing with cmd.exe it works like a charm. This is not very clean however, and requires me knowing the cert name when creating the task. There are a million ways I can overcome this issue but for the life of me I don't understand why it doesn't just work from .NET while all other commands I require are working fine.
So, this is not really a PoSH specific thing natively. That error message is more general than specific to .Net use case.
This error message is not unique to what you at doing, especially most recently on Win10. This error has been reported, since the 1703 release, even just when doing things on the file system, like WDS captures, folder creation etc.
Several reasons have been postulated, permissions and the like, but all had to just come up with a workaround (permission fixes, etc.) as you have indicated the you have a million ways to address your use case.
But to get to potential root cause, you need to look deeper at the other Windows event log information, Application, Security, System and PowerShell logs from when you are doing your .Net effort.
Problem Description
I have a Windows service which is hosting an NServiceBus endpoint in NServiceBus.Host.exe.
The binaries are deployed to c:\inetpub\bus\services\myService folder on the server.
A DSC script makes sure the Windows service is created/exists on the server, with the "path to executable" property of the service set to "c:\inetpub\bus\services\myService´NServiceBus.Host.exe" -service NServicebus.Production.
Note! The -service switch is added when installing the service by using the built-in NServiceBus.Host.exe /install parameter, which is why I added it to the Windows service executable path in the DSC script.
Now, when I try to start the service manually on the server, it yields the following error message
Windows could not start the <service name> service on the Local Computer.
Error 1053: The service did not respond to the start or control request in a timely fashion.
Debugging Steps
I have looked through the event log and the following two error messages sticks out:
NServiceBust.Host.exe error:
Application: NServiceBus.Host.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info:
Topshelf.Exceptions.ConfigurationException
at Topshelf.Internal.Actions.RunAsServiceAction.Do(Topshelf.Configuration.IRunConfiguration)
at Topshelf.Runner.Host(Topshelf.Configuration.IRunConfiguration, System.String[])
at NServiceBus.Host.Program.Main(System.String[])
Local Activation permission error:
The application-specific permission settings do not grant Local
Activation permission for the COM Server application with CLSID
{D63B10C5-BB46-4990-A94F-E40B9D520160}
and APPID
{9CA88EE3-ACB7-47C8-AFC4-AB702511C276}
to the user <my_service_account_user> SID (<service_account_SID>) from
address LocalHost (Using LRPC) running in the application container
Unavailable SID (Unavailable). This security permission can be modified
using the Component Services administrative tool.`
Note! The error above only occurs once, i.e. the first time I try to start the service. It does not appear again in the event log for any subsequent attempts of starting the service.
What I have done so far:
Tried the suggestions in a closely related post here on SO, none of which were working.
Tried to install the service by using using the NServiceBus.Host.exe /install parameter. In this case, the service name is created with its name on the following format: MyService.EndpointConfig_v1.0.0.0. Using this approach, the service starts successfully without any error message
Stopping the service and then try to start the service created by the DSC script (with a different name) => success
Removing the service created by NServiceBus and then trying to start the DSC-created service again => failure
Tried granting the service account used for logon when running the service various privileges (neither of which yielded any success), among others:
Membership in the Administrators group
Membership in the Performance Log Users group
Full DCOM permissions via "Launch and Activation Permissions" in dcomcnfg
Tried running c:\inetpub\bus\services\myService´NServiceBus.Host.exe NServicebus.Production from the CLI => success
Code
My Init() method for the service looks like this:
namespace MyService
{
public class EndpointConfig : IConfigureThisEndpoint, AsA_Server, IWantCustomLogging, IWantCustomInitialization
{
public void Init()
{
Directory.SetCurrentDirectory(System.AppDomain.CurrentDomain.BaseDirectory);
SetLoggingLibrary.Log4Net(() => XmlConfigurator.Configure(File.OpenRead(#"log4net.config")));
GlobalContext.Properties["Hostname"] = Dns.GetHostName();
GlobalContext.Properties["Service"] = typeof(EndpointConfig).Namespace;
var container = new WindsorContainer(new XmlInterpreter());
Configure.With()
.CastleWindsorBuilder(container)
.XmlSerializer()
.MsmqTransport()
.IsTransactional(true)
.PurgeOnStartup(false)
.IsolationLevel(System.Transactions.IsolationLevel.RepeatableRead);
var connectionString = ConfigurationManager.ConnectionStrings["<some_conn_string>"].ConnectionString;
container.Register(
Component.For<IDatabaseProvider>()
.ImplementedBy<DatabaseProvider>()
.DependsOn(Property.ForKey("connectionString").Eq(connectionString)));
}
}
}
Theory
When installing the service using /install I assume that NServiceBus.Host.exe does some black magic under the hood - e.g. grants some necessary permissions - to make the service able to start.
Note! On the server, the latest version of NServiceBus is installed (v6.x). However, in my solution/service project version 2.x is used (please, do not ask if I can upgrade - unfortunately, that is not an option).
Appreciate any help I can get, as I am running out of ideas.
EDIT 1
I was asked why I can't just use the /install parameter of NServiceBus and be happy with that. The answer to that is that I could (and, actually, I currently am).
The reason I have still posted this question is split:
I wish to understand why one of two seemingly equivalent approaches fails
I am not completely happy with using the /install parameter. The reason? It boils down to a "chicken or the egg" problem. I use Powershell DSC to provision servers in Azure and I believe that ensuring that Windows Services exists on the server is the responsibility of DSC. However, the first time a server is provisioned the services cannot exist unless I script their creation with DSC, and point the executable path to where the service binaries will be deployed whenever that happens. The other alternative is to skip service creation in DSC, and run the NServiceBus.Host.exe /install as a part of the service/application deployment script instead. Obviously, deployment cannot happen until after a server has been provisioned. Thus, it requires the Windows Service part of the DSC script being stripped down to e.g. merely ensuring the service exist - a verification which will fail until a first time deployment of the application has been performed.
I have a console application that will optionally self-install itself as a service. This works fine, but I'd like to embed some arguments into the service startup - similar to (for example) Google's Update Service (which has the parameter /medsvc)
So let's say I'd like my service to start
MyService.exe RUN Test1
.. so that'd start up MyService.exe with the parameters RUN and Test1.
I can install the service fine, using
ManagedInstallerClass.InstallHelper(new[] {Assembly.GetExecutingAssembly().Location});
However, there's no parameters on the service. So if I try:
ManagedInstallerClass.InstallHelper(new[] {Assembly.GetExecutingAssembly().Location +" RUN Test1"});
I get a FileNotFoundException. Giving that it's a array, I thought I'd try:
ManagedInstallerClass.InstallHelper(new[] {Assembly.GetExecutingAssembly().Location,"RUN","Test1"});
.. which gives the same exception, except that it's trying to find the file RUN now.
I can't find any specific documentation on how to achieve this - does anyone know if it is possible to embed parameters in with the service executable path? As another example, here's Google's Update Service with parameters - I'd like to ultimately achieve the same.
It took me a while to find this out, I hope it's still useful to someone.
First I found out, that you are not supposed to run ManagedInstallerClass.InstallHelper according to MSDN docs:
This API supports the product infrastructure and is not intended to be
used directly from your code.
Then I found out I could just use my own ProjectInstaller (a component class I added containing a Service Installer and a Service Process Installer) to install the service like this:
ProjectInstaller projectInstaller = new ProjectInstaller();
string[] cmdline = { string.Format("/assemblypath={0} \"/myParam\"", Assembly.GetExecutingAssembly().Location) };
projectInstaller.Context = new InstallContext(null, cmdline);
System.Collections.Specialized.ListDictionary state = new System.Collections.Specialized.ListDictionary();
projectInstaller.Install(state);
Be sure to encapsulate your parameters in quotes and escape the quotes, otherwise your parameters will become part of the executable path and fail to start.
The end result will be a new service with the specified properties in your Service Installer and Service Process Installer, and a path just like in your screenshot (with the /medsvc parameter for example).
I use a console application inside my windows service. The Main method in Program.cs processes the command line args. The OnStart method starts the console application. It works great.
Windows Service to Run Constantly
HybridService Easily Switch Between Console Application and Service
Only parameters before location are being passed into the context for the installer.
Try this:
args = new[] { "/ServiceName=WinService1", Assembly.GetExecutingAssembly().Location };
ManagedInstallerClass.InstallHelper(args);
Reference from another answer: Passing Parameter Collection to Service via InstallHelper
I have a test-project which performs WebDriverTests.
At this moment I have the following in my code:
private readonly string url = #"http://localhost:3000/#/search/persons";
My application (including the webdrivertests) is deployed using TeamCity (installed on PC-A) and deploys the application to PC-B.
When the deployment is done, I want to run my webdrivertests on PC-B as part of the TeamCity-deployment. But with the above mentionned line this doesn't work since the application isn't deployed on the server where my TeamCity is running.
Is there a way to do this using an app.config?
Or is there another way?
Absolutely. Richard Bradshaw (#FriendlyTester on Twitter) has a great approach to handle this. I use the same approach, Richard just has an existing great blogpost I can point you to.
When are workflow agents actually called?
I've installed my own workflow agent (this one) and write to a log on the second line in ProcessWorkflow (the first one being the log4net XmlConfigurator.Configure call with a newly created FileInfo instance.
The log is always written after the KTM Server module. This WOULD make sense, because I read a configuration which prompts the WFA to do something with the workflow data. But after the KTM Validation module (where the WFA is also configured to do something) the log is not written.
Is there an explanation, why I don't see any log entries? I've checked the kofax logs too, but I found no evidence there.
The exact code snippet looks like this:
public void ProcessWorkflow(ref IACWorkflowData workflowData)
{
XmlConfigurator.Configure(new FileInfo(#"C:\Program Files (x86)\Kofax\CaptureSS\ServLib\Configuration Files\log4net.config"));
log.Info("Workflow Agent started ...");
// rest of the code
So, since I kind of figured out how to use Workflow Agents, I decided to answer this question for future reference.
A Workflow Agent is being run every time a module has been executed. IIRC this includes viewing the properties with Batch Manager. The Workflow Agent will be called on the site where the module has been executed. So if you execute your automatic modules (i.e. PDF Generator, Export) on a server and Scan and Validation on client sites, the Workflow Agent will be executed on the server or the client station which executed the module respectively.
I actually forgot what didn't work in my original question, but I also ran into problems because I didn't register the DLL using RegAsm.exe. See my other Kofax-related question for more information about this: How to correctly install Workflow Agents in Kofax?
You can also use this in your code so that it only runs the logic when you want it to:
if (workflowData.CurrentModule.Name != "Scan" || workflowData.get_NextState().Name != "Ready")
{
return;
}