Quartz.net as service can't configure the quartz_job.xml - c#

When using
var properties = new NameValueCollection();
properties["quartz.plugin.triggHistory.type"] = "Quartz.Plugin.History.LoggingJobHistoryPlugin";
properties["quartz.plugin.jobInitializer.type"] = "Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin";
properties["quartz.plugin.jobInitializer.fileNames"] = "quartz_jobs.xml";
properties["quartz.plugin.jobInitializer.failOnFileNotFound"] = "true";
properties["quartz.plugin.jobInitializer.scanInterval"] = "120";
// First we must get a reference to a scheduler
_schedulerFactory = new StdSchedulerFactory(properties);
_scheduler = _schedulerFactory.GetScheduler();
The windows service / quartz cannot resolve the path of quartz_jobs.xml.
If i run this as console it works fine.
public static void StartJobs()
{
try
{
_logger = LogManager.GetCurrentClassLogger();
var properties = new NameValueCollection();
properties["quartz.plugin.triggHistory.type"] = "Quartz.Plugin.History.LoggingJobHistoryPlugin";
properties["quartz.plugin.jobInitializer.type"] = "Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin";
properties["quartz.plugin.jobInitializer.fileNames"] = "quartz_jobs.xml";
properties["quartz.plugin.jobInitializer.failOnFileNotFound"] = "true";
properties["quartz.plugin.jobInitializer.scanInterval"] = "120";
// First we must get a reference to a scheduler
_schedulerFactory = new StdSchedulerFactory(properties);
_scheduler = _schedulerFactory.GetScheduler();
// start the schedule
_scheduler.Start();
}
catch (Exception ex)
{
_logger.Error(ex);
throw new Exception(ex.Message);
}
}

If it's still not working, include the file as an embedded resource in the project, set the action to Copy always, to be sure. Then provide the full file path to the quartz property:
Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "quartz_jobs.xml")

I know this thread is from 2015, but i can't find any information regarding using quartz.net in a windows service. In my case, I am using .Net Core 2.1 Generic Host as a windows service with the quartz_jobs.xml which is referenced in my appsettings.json file. When the windows service starts up an looks for the quartz_job.xml it tries to find it in c:\windows\system32. But my quartz_job.xml is located where my executable is located. I tracked down Method ResolveFile in Quaztz\Util\FileUtil.cs of their repo where is says to put a "~" to for relative file. So I changed my appsettings.json to
"plugin": {
"jobInitializer": {
"type": "Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz.Plugins",
"fileNames": "~\\quartz_jobs.xml"
}
and now the windows service is able to read the quartz_jobs.xml. I would expect the if you change
properties["quartz.plugin.jobInitializer.fileNames"] = "quartz_jobs.xml";
to
properties["quartz.plugin.jobInitializer.fileNames"] = "~\\quartz_jobs.xml";
it should also work.

Related

ServerManager fails with 0x80040154 in c# Winforms app creating simple web application in IIS

I am writing a small app that installs IIS and configures a website before deploying the files to it.
On a freshly reset Windows 10, the first attempt always fails with the 0x80040154 COM+ component failure as documented in This question
I looked at the version I am using and it is the latest and correct one for .net standard (4.8) and not the one meant for .net core
When I press the button to rerun the function it always finishes correctly. I tried using a retry routine, and it fails on each retry, yet runs fine again when the button is pressed. The reason for this I assume is that the server manager object isn't disposed when it hits the catch block since its in a using statement.
I can work around that, but I really want to understand the issue and make a permanent fix.
My routine simply creates a website in IIS and creates an app pool to assign to it.
And it is running with elevated privileges
For reference:
Machine is Windows 10 latest from the downloadable media creator.
Microsoft.Web.Administrator version is 7.0.0.0
App is .net 4.8 standard windows forms
using (var serverManager = new ServerManager())
{
string iisrootdir = drive;
//Check for inetpub/wwwroot
if (!Directory.Exists(iisrootdir)) //Check for Drive D
{
iisrootdir = #"C:\";
}
string iiscmsdir = Path.Combine(iisrootdir, "webdir", "appdir");
if (!Directory.Exists(iiscmsdir))
Directory.CreateDirectory(iiscmsdir);
var settings = new ApplicationSettings();
settings.ReadFromFile();
settings.CMSPATH = iiscmsdir;
settings.SaveToFile();
try
{
string poolName = "DefaultAppPool";
if (serverManager.Sites.Count > 0)
{
Site myDefualtWebsite = serverManager.Sites[0];
if (myDefualtWebsite != null)
{
OnRaiseInstallEvent(new InstallEventArgs("CreateWebsite", ProcessState.Started,
"Remove Default Website"));
serverManager.Sites.Remove(myDefualtWebsite);
serverManager.CommitChanges();
}
}
if (!WebsiteExists("sitename"))
{
mySite.ServerAutoStart = true;
}
Site site = serverManager.Sites["sitename"];
if (!AppPoolExists(poolName))
{
serverManager.ApplicationPools.Add(poolName);
}
ApplicationPool apppool = serverManager.ApplicationPools[poolName];
apppool.ManagedPipelineMode = ManagedPipelineMode.Integrated;
apppool.ManagedRuntimeVersion = "";
serverManager.Sites["sitename"].ApplicationDefaults.ApplicationPoolName = poolName;
foreach (var item in serverManager.Sites["sitename"].Applications)
{
item.ApplicationPoolName = poolName;
}
serverManager.CommitChanges();
apppool.Recycle();
serverManager.CommitChanges();
}
catch (Exception ex)
{
if (ex.Message.Contains("80040154") && errorCount < 4)
{
if (serverManager != null)
serverManager.Dispose();
errorCount++;
OnRaiseInstallEvent(new InstallEventArgs("CreateWebsite", ProcessState.Started,
"Error encountered with COM+ object, trying again: " + errorCount));
CreateWebsite(#"D:\");
}
else
{
if (serverManager != null)
serverManager.Dispose();
errorCount = 0;
OnRaiseErrorEvent(new InstallErrorEventArgs("CreateWebsite", ProcessState.Error, ex));
return false;
}
}
finally
{
serverManager?.Dispose();
}
Thanks for the help Guys. I found the problem.
DISM was running in its own thread. The Process object exited the moment it launched. My function was then attempting to configure IIS before it had finished installing.

Check out files from TFS from a web service

I've read several posts here about replacing the obsolete TeamFoundationServer with TfsTeamProjectCollectionFactory but not in a web service. The obsolete method works in my web service but the TfsTeamProjectCollectionFactory method does not. It always throws an exception setting the workspace variable:
There is no working folder mapping for C:\MyPath
The web service uses an app pool assigned to my identity which is a local machine admin and the new method works if I make it a console app.
Is it doable to replace TeamFoundationServer in a web service?
void TfsCheckout(string tfsServer, string wkSpace, string fileName)
{
try
{
//new method
var pc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(tfsServer));
var versionControlServer = (VersionControlServer)pc.GetService((typeof(VersionControlServer)));
var workspace = versionControlServer.GetWorkspace(wkSpace);
var result = workspace.PendEdit(fileName);
//obsolete method
var tfs = new TeamFoundationServer(tfsServer, new UICredentialsProvider());
var versionControl = (VersionControlServer)tfs.GetService(typeof(VersionControlServer));
var workspc = versionControl.GetWorkspace(Environment.MachineName, versionControl.AuthenticatedUser);
result = workspc.PendEdit(fileName);
UpdateStatus(new UpdateStatusEventArgs("Checkout: " + fileName));
UpdateStatus(new UpdateStatusEventArgs("Result: " + result + " (1=success 0=fail)"));
}
catch (Exception ex)
{
UpdateStatus(new UpdateStatusEventArgs(ex.ToString()));
UpdateStatus(new UpdateStatusEventArgs("Done"));
}
}

Windows Service: Specified eventSource exists already

I'm writing a Windows Service in C# (.NET 4).
Here is the code for the installer:
[RunInstaller(true)]
public partial class JobManagerInstaller : Installer
{
public JobManagerInstaller()
{
InitializeComponent();
this.Installers.Clear();
ServiceProcessInstaller serviceProcessInstaller = new ServiceProcessInstaller();
ServiceInstaller serviceInstaller = new ServiceInstaller();
EventLogInstaller eventLogInstaller = new EventLogInstaller();
// Service Account Information
serviceProcessInstaller.Account = ServiceAccount.LocalSystem;
serviceProcessInstaller.Username = null;
serviceProcessInstaller.Password = null;
// Service Information
// The installer's ServiceName must be identical to the JobManager.ServiceName set in the constructor of JobManager.cs
serviceInstaller.ServiceName = "VIAVista";
serviceInstaller.DisplayName = "VIAVista";
serviceInstaller.StartType = ServiceStartMode.Automatic;
// EventLog
eventLogInstaller.Source = "VIAVista";
eventLogInstaller.Log = "VIAVista";
// Dependency SQL Server service (i.e.SQL Server must run)
serviceInstaller.ServicesDependedOn = new string[] { "MSSQL$SQLEXPRESS" };
this.Installers.Add(serviceProcessInstaller);
this.Installers.Add(serviceInstaller);
this.Installers.Add(eventLogInstaller);
}
}
As you can see I want my event's source and log to be named "VIAVista".
When I try to install the service on my server (Windows Web Server 2008 R2 64bit) I'm told that the event source already exists in log "Application". That's weird since I thought this.Installers.Clear() would prevent creating a default source/log.
Info: I used regedit to make sure that there is no "VIAVista" key before installing the service.
Any ideas? Did I miss anything?
Try
serviceInstaller.Installers.Clear();
Do this:
if (!EventLog.SourceExists(source))
EventLog.CreateEventSource(source, log);
EventLog.WriteEntry(source, message, type, eventid);
If the source exists it will be used, otherwise it will be created.

Can I obtain the ClickOnce published Product Name from inside the application?

I have a ClickOnce Publish Name that is different from the assembly name. For discussion purposes, it is "App 6.0". I set it in the Properties for my project. Is there any way to get this value from inside the program?
Add a reference to Microsoft.Build.Tasks.v4.0.dll, then run this:
if (null != AppDomain.CurrentDomain.ActivationContext)
{
DeployManifest manifest;
using (MemoryStream stream = new MemoryStream(AppDomain.CurrentDomain.ActivationContext.DeploymentManifestBytes))
{
manifest = (DeployManifest)ManifestReader.ReadManifest("Deployment", stream, true);
}
// manifest.Product has the name you want
}
else
{
// not deployed
}
The DeployManifest can also provide other useful info from your manifest, like Publisher or SupportUrl.
The answer can be found in ClickOnce Run at Startup. Essentially, you use InPlaceHostingManager to get the ClickOnce manifest and read it. It bugs me that it is an asynchronous method, but this is the only thing that has worked thus far. Simplifications are much appreciated. See the webpage for a description of DeploymentDescription.
var inPlaceHostingManager = new InPlaceHostingManager(ApplicationDeployment.CurrentDeployment.UpdateLocation, false);
inPlaceHostingManager.GetManifestCompleted += ((sender, e) =>
{
try
{
var deploymentDescription = new DeploymentDescription(e.DeploymentManifest);
string productName = deploymentDescription.Product;
***DoSomethingToYour(productName);***
// - use this later -
//var commandBuilder = new StartMenuCommandBuilder(deploymentDescription);
//string startMenuCommand = commandBuilder.Command;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + Environment.NewLine + ex.StackTrace);
}
});
ApplicationDeployment.UpdatedApplicationFullName Property

When programmatically creating a new IIS web site, how can I add it to an existing application pool?

I have successfully automated the process of creating a new IIS website, however the code I've written doesn't care about application pools, it just gets added to DefaultAppPool. However I'd like to add this newly created site to an existing application pool.
Here is the code I'm using to create the new website.
var w3Svc = new DirectoryEntry(string.Format("IIS://{0}/w3svc", webserver));
var newsite = new object[] { serverComment, new object[] { serverBindings }, homeDirectory };
var websiteId = w3Svc.Invoke("CreateNewSite", newsite);
site.Invoke("Start", null);
site.CommitChanges();
<update>
Although this is not directly related to the question, here are some sample values being used above. This might help someone understand exactly what the code above is doing more easily.
webServer: "localhost"
serverComment: "testing.dev"
serverBindings: ":80:testing.dev"
homeDirectory: "c:\inetpub\wwwroot\testing\"
</update>
If I know the name of the application pool that I'd like this web site to be in, how can I find it and add this site to it?
You have to assign the AppPool on the virtual dir (not the webserver) and set the AppIsolated property to 2 which mean pooled-process ;)
http://msdn.microsoft.com/en-us/library/ms525598%28v=VS.90%29.aspx
Relevant code sample from link:
static void AssignVDirToAppPool(string metabasePath, string appPoolName)
{
// metabasePath is of the form "IIS://<servername>/W3SVC/<siteID>/Root[/<vDir>]"
// for example "IIS://localhost/W3SVC/1/Root/MyVDir"
// appPoolName is of the form "<name>", for example, "MyAppPool"
Console.WriteLine("\nAssigning application {0} to the application pool named {1}:", metabasePath, appPoolName);
try
{
DirectoryEntry vDir = new DirectoryEntry(metabasePath);
string className = vDir.SchemaClassName.ToString();
if (className.EndsWith("VirtualDir"))
{
object[] param = { 0, appPoolName, true };
vDir.Invoke("AppCreate3", param);
vDir.Properties["AppIsolated"][0] = "2";
Console.WriteLine(" Done.");
}
else
Console.WriteLine(" Failed in AssignVDirToAppPool; only virtual directories can be assigned to application pools");
}
catch (Exception ex)
{
Console.WriteLine("Failed in AssignVDirToAppPool with the following exception: \n{0}", ex.Message);
}
}
Note that if you are not explicitly adding a new virtual directory to the application, the metabasePath will simply be "IIS://<servername>/W3SVC/<siteID>/Root"
You need to get the AppPoolfrom IIS://{0}/W3SVC/AppPools, and attach it to the site's AppPoolId. Something like:
var appPool = new DirectoryEntry(
string.Format("IIS://{0}/W3SVC/AppPools/{1}", webServer, appPoolName)
);
site.Properties["AppPoolId"].Value = appPool;
site.Properties["AppPoolId"][0]= "poolname";

Categories