Wix - Managed Bootstrapper Application upgrade problem - c#

I decided to ask my question here because I can't find answer in any other place.
My task is to prepare upgrade process with managed bootstrapper UI, but it doesn't want to work properly.
I prepared all of needed MSI packages. I mean I added Product Id="*", I added MajorUpgrade tag and configured it and I change versions between bundle(for test purposes), I also added a few properties which should helps me with distinguish is it an Install, Uninstall or Upgrade process.
And my problem starts here, because when I was using default burn UI it worked properly I mean during installation property _INSTALL was set to 1, during upgrade (installing version 2 of bundle) property _UPGRADE was set to 2 and the same with uninstallation, but now when I added Custom UI to that, UPGRADE property isn't set at all. Instead of that during trial of UPGRADE first starts Installation process and it goes to some point and then new window with Uninstallation appears.
My question is can I somehow make my Custom UI to behave like a Default UI for burn?

Thank you everyone for comments. Inspired by How to perform Wix Upgrade with custom bootstrapper I understood I didn't handle situation when installer is run quietly.
So I prepared another class for SilentUninstall and did it in my Bootstrapper class:
public class BootstrapperApp : BootstrapperApplication
{
public static Dispatcher Dispatcher { get; set; }
protected override void Run()
{
Dispatcher = Dispatcher.CurrentDispatcher;
var model = new BootstrapperApplicationModel(this);
var command = model.BootstrapperApplication.Command;
if (command.Action == LaunchAction.Uninstall && (command.Display == Display.None || command.Display == Display.Embedded))
{
model.LogMessage("Starting silent uninstaller.");
var viewModel = new SilentUninstallViewModel(model, Engine);
Engine.Detect();
}
else
{
model.LogMessage("Starting installer.");
var viewModel = new InstallViewModel(model);
var view = new InstallView(viewModel);
view.Closed += (sender, e) => Dispatcher.InvokeShutdown();
model.SetWindowHandle(view);
Engine.Detect();
view.Show();
}
Dispatcher.Run();
Engine.Quit(model.FinalResult);
}}
and my SilentUninstaller class:
public class SilentUninstallViewModel
{
private BootstrapperApplicationModel model;
private Engine engine;
public SilentUninstallViewModel(BootstrapperApplicationModel model, Engine engine)
{
this.model = model;
this.engine = engine;
WireUpEventHandlers();
}
private void WireUpEventHandlers()
{
this.model.BootstrapperApplication.PlanComplete += PlanCompleted;
this.model.BootstrapperApplication.DetectComplete += DetectCompleted;
this.model.BootstrapperApplication.ApplyComplete += ApplyCompleted;
}
private void DetectCompleted(object sender, DetectCompleteEventArgs e)
{
this.model.LogMessage("Detecting has been completed for silent uninstallation.");
this.model.PlanAction(LaunchAction.Uninstall);
}
private void ApplyCompleted(object sender, ApplyCompleteEventArgs e)
{
this.model.LogMessage("Applying has been completed for silent uninstallation.");
this.model.FinalResult = e.Status;
this.engine.Quit(this.model.FinalResult);
}
private void PlanCompleted(object sender, PlanCompleteEventArgs e)
{
this.model.LogMessage("Planning has been started for silent uninstallation.");
model.ApplyAction();
}
}
and it seems to works properly. Even property _UPGRADE is raised in particular MSIs ;)

Related

How can I debug a Xamarin watchOS Linker Error

I am developing an iOS App using Xamarin Forms for which I have created a core model around which all app functionality works.
I would like to include a simple watchOS app which allows the user to operate on a single instance of this model at any one time. I have implemented some code to update the model in the watchOS App using WCSession (via this WCSessionManager Class). I have also reused some code for implementing a timer from my Xamarin Forms project.
However I am encountering a Linker error when building my solution. I think it may be because I have referenced my Xamarin Forms project from my watchOS project, which may not be allowed. Here is the error:
/Users/luketimothy/Projects/TodoQ/TodoQ.Watch/TodoQ.Watch.WatchOSExtension/MTOUCH: Error MT2001: Could not link assemblies. Reason: Error while processing references of 'TodoQWatchWatchOSExtension, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' (MT2001) (TodoQ.Watch.WatchOSExtension)
The file the error references is MTOUCH. I am not sure what this is exactly, but the only place in my watchOS app I have referenced my Xamarin Forms code is this object:
using System;
using System.Collections.Generic;
using TodoQ.Models;
using TodoQ.Utilities;
using WatchConnectivity;
using WatchKit;
namespace TodoQ.Watch.WatchOSExtension
{
internal class TodoState
{
private TodoItem current;
private ISessionTimer timer;
public TodoItem Current { get => current; set { current = value; TaskUpdated(this, value); } }
public event TaskUpdatedEventHandler TaskUpdated;
public delegate void TaskUpdatedEventHandler(object sender, TodoItem current);
public event TimerElapsedEventHandler TimerElapsed;
public delegate void TimerElapsedEventHandler(object sender, TimerElapsedEventArgs current);
public TodoState()
{
WCSessionManager.SharedManager.ApplicationContextUpdated += DidReceiveApplicationContext;
timer = new PomodoroTimer();
timer.ProgressUpdate += (object sender, ProgressUpdateEventArgs e) =>
{
TimerElapsed(this, new TimerElapsedEventArgs() { Elapsed = e.Elapsed, EndTime = e.EndTime });
};
timer.MilestoneUpdate += (object sender, PomodoroStateID e) =>
{
var audio_file = WKAudioFilePlayerItem.Create(WKAudioFileAsset.Create(new Foundation.NSUrl("ShortBreak.wav")));
var audio_player = WKAudioFilePlayer.Create(audio_file);
audio_player.Play();
WKInterfaceDevice.CurrentDevice.PlayHaptic(WKHapticType.Notification);
};
}
public void DidReceiveApplicationContext(WCSession session, Dictionary<string, object> applicationContext)
{
var message = (TodoItem)applicationContext["FocusedItem"];
if (message != null)
{
Console.WriteLine($"Application context update received : {message.Heading}");
Current = message;
}
}
public void StartTimer()
{
timer.StartSession();
}
}
public class TimerElapsedEventArgs
{
public TimeSpan Elapsed;
public TimeSpan EndTime;
}
}
So, my question is. If this ought to be allowed, and the error is something else, could I get some help tracking down what this MTOUCH is and why it's throwing this error? If it is not allowed, what is the recommended solution for sharing this kind of code between my Phone App and my Watch App? Could I put it in a PCL? Should I copy the code between projects?
You should not reference your WatchOS project to the Forms project. It should be added in iOS project directly.
And if you want to define some common code for reuse. You could create a shared library:
Add some public classes there:
namespace UtiLibrary
{
public static class UtiClass
{
public static List<Model> datas { get => new List<Model> { new Model { Name = "name" } }; }
}
public class Model
{
public string Name { set; get; }
}
}
Then you could utilize it on each platform which has referenced this library.

WMNetMgr.dll Application Error w3wp.exe error using WMPLib C# .NET

I am having an issue with my web application regularly crashing and resetting the application pool in IIS causing big performance issues, as well as wiping any timing threads running in my application.
The site is a .NET 4.5.2 C# MVC5 Site running on a 2012 Windows Server EC2 instance in AWS.
The issue was first noticed when I started see the site struggle to load after so many minutes of run-time. I thought it might be the ApplicationPool recycling and made sure to set IdleTime and Application Preload in IIS properly. The issue still persisted.
Next I went to the Server Manager to check the event logs and found these entries happening about every 15 minutes or so:
Faulting application name: w3wp.exe, version: 8.5.9600.16384, time
stamp: 0x5215df96 Faulting module name: WMNetMgr.dll_unloaded,
version: 12.0.9600.17415, time stamp: 0x545047db Exception code:
0xc0000005 Fault offset: 0x00000000000cf5cf Faulting process id:
0x17d0 Faulting application start time: 0x01d331dc20f096d0 Faulting
application path: c:\windows\system32\inetsrv\w3wp.exe Faulting module
path: WMNetMgr.dll Report Id: 777a35de-9dd1-11e7-81d7-025ff0be916d
Faulting package full name: Faulting package-relative application ID:
and
WIN-7PCRJOFR05F 5011 Warning Microsoft-Windows-WAS System 9/20/2017
7:01:04 AM - A process serving application pool 'SiteName' suffered a
fatal communication error with the Windows Process Activation Service.
The process id was '6096'. The data field contains the error number.
Next I ran a DebugDiag2 Collection and Analysis:
WARNING - DebugDiag was not able to locate debug symbols for
WMNetMgr.dll>, so the information below may be incomplete.
In
w3wp__SiteName__PID__5088__Date__09_20_2017__Time_06_31_02AM__436__Second_Chance_Exception_C0000005.dmp
an access violation exception (0xC0000005) occured on thread 26 when
another Module attempted to call the following unloaded Module:
WMNetMgr.dll>.
Thread 26:
Call Stack Unloaded_WMNetMgr.dll+cf5cf
0x000000de575cf7c0 0x000000dc2ed5ec10
This is the only error reported by this debugger. With no others exceptions in the .NET stack trace on the report. I can't seem to get the debugging symbols for this particular .dll and the messages do not seem to be very helpful.
The application utilizes WMPLib to create a singleton instance at startup of the wmplayer to play sounds on the Windows Server 2012 instance via web requests from clients. The application works in this regard with no issue playing sounds and requests from multiple users.
Here is the Singleton:
public sealed class SoundboardSingleton : IDisposable
{
private static readonly Lazy<SoundboardSingleton> lazy =
new Lazy<SoundboardSingleton>(() => new SoundboardSingleton());
public static SoundboardSingleton Instance { get { return lazy.Value; } }
public WindowsMediaPlayer WMPlayer;
private StayAliveBot _liveBot;
private Thread _botThread;
private SoundboardSingleton()
{
WMPlayer = new WindowsMediaPlayer();
WMPlayer.settings.volume = 50;
_liveBot = new StayAliveBot();
_botThread = new Thread(_liveBot.Live);
_botThread.Start();
}
public void Dispose()
{
if (_botThread.IsAlive)
{
_botThread.Abort();
}
}
}
public class StayAliveBot
{
public void Live()
{
while (SoundboardSingleton.Instance != null)
{
Thread.Sleep(1500000);
SoundboardHelper.PlaySound("C:\\SoundboardOpFiles\\TestTone.wav");
}
}
}
and initially instantiated in Startup.cs via:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
// startup soundboard singleton
SoundboardSingleton.Instance.WMPlayer.settings.volume = 50;
}
}
I can run this application on my local dev machine with no issues or crashes. Everything functions as expected with no crashing. On deployment to the EC2 instance, everything on the site works properly, but there is now a crash / reset every 15 minutes.
My suspicion is either:
A) It is a problem with the WMPLib instance and some missing dependency on the Windows Server 2012 box that allows it to play sounds but causes crashes on regular intervals.
B) I've made a mistake with my singleton instantiation and it is somehow crashing my application.
I have tried the solution here but no results.
Any help would be appreciated.
edit: I have confirmed the issue is related to the usage of WMPLib as removing its use stopped the crashing every 15 minutes. Still not sure why this happens.
This is not a direct answer to your question but rather a different way do to the same thing. Instead of the WMPLib COM control, try using the thread-safe MediaPlayer class from WPF. Add references to WindowsBase and PresentationCore, and use something like this instead:
using System.Windows.Media;
public void PlaySound(string filename)
{
var mplayer = new MediaPlayer();
mplayer.MediaEnded += new EventHandler(MediaEndedHandler);
mplayer.Open(new Uri(filename));
mplayer.Play();
}
public void MediaEndedHandler(object sender, EventArgs e)
{
((MediaPlayer)sender).Close();
}
You can also use it as a singleton the same way as above, and it is fully thread-safe, which WMPLib is not.
Documentation here.
Edit:
As noted in the comments, you really could use just a static class with a public bool property to show busy signal. A static class in IIS is shared among all requests for an application and the class is only subject to garbage collection when the application pool is recycled, so you do need to be careful with the lifetime of the objects you store in it, to avoid memory consumption problems. This code will use a new instance of the media player class for each PlaySound() and disposes of it right after it's done playing, but the busy flag is common among all requests made to the server.
using System;
using System.Threading;
using System.Windows.Media;
namespace SoundBoardApp
{
public static class Soundboard
{
private static bool _isBusy = false;
public static bool IsBusy { get { return _isBusy; } }
private static void MediaEndedHandler(object sender, EventArgs e)
{
_isBusy = false;
var wmp = ((MediaPlayer)sender);
wmp.MediaEnded -= new EventHandler(MediaEndedHandler);
wmp.Close();
}
public static bool PlaySound(string filename)
{
if (!_isBusy)
{
_isBusy = true;
var wmp = new MediaPlayer();
wmp.MediaEnded += new EventHandler(MediaEndedHandler);
wmp.Volume = 0.5;
wmp.Open(new Uri(filename));
wmp.Play();
return true;
}
else
{
return false;
}
}
}
public class StayAliveBot
{
public void Live()
{
while (true)
{
Thread.Sleep(1500000);
if (!Soundboard.IsBusy) Soundboard.PlaySound("C:\\SoundboardOpFiles\\TestTone.wav");
}
}
}
}
I ended up using NAudio with my singleton pattern.
As per recommendations from Lex Li, I used a third party as the Windows.MediaPlayer was not meant for a web app. Based of the solution from Drunken Code Monkey, I used a boolean flag on the singleton to evaluate play states being checked frequently by a separate thread evaluating the PlaybackState.Stopped value on the IWavePlayer object in my singleton. My only concern is performance. I haven't noticed any problems yet, but I'm pretty sure managing Events in a Handler would have been more performant and a lot less code if it's even possible to do from a web app.
Here is the code:
using NAudio.Wave;
public sealed class SoundboardSingleton : IDisposable
{
private static readonly Lazy<SoundboardSingleton> lazy =
new Lazy<SoundboardSingleton>(() => new SoundboardSingleton());
public static SoundboardSingleton Instance { get { return lazy.Value; } }
public IWavePlayer WaveOutDevice { get; set; }
public AudioFileReader AudioFileReaderObj { get; set; }
public float Volume { get; set; }
private MediaCloser _mediaCloser;
private Thread _mediaCloserThread;
private StayAliveBot _liveBot;
private Thread _botThread;
public bool IsBusy { get; set; }
private SoundboardSingleton()
{
// checks our NAudio WaveOutDevice playback for stop
_mediaCloser = new MediaCloser();
_mediaCloserThread = new Thread(_mediaCloser.CheckForStoppedPlayback);
_mediaCloserThread.Start();
// thread to play sound every 25 minutes, to avoid idle flag
_liveBot = new StayAliveBot();
_botThread = new Thread(_liveBot.Live);
_botThread.Start();
}
public bool PlaySound(string filename)
{
// make sure we are not active
if (IsBusy) { return false; }
// process sound
IsBusy = true;
WaveOutDevice = new WaveOutEvent();
AudioFileReaderObj = new AudioFileReader(filename);
AudioFileReaderObj.Volume = Volume;
WaveOutDevice.Init(AudioFileReaderObj);
WaveOutDevice.Play();
return true;
}
public void CloseWaveOut()
{
// clean up sound objects
WaveOutDevice?.Stop();
if (AudioFileReaderObj != null)
{
AudioFileReaderObj.Dispose();
AudioFileReaderObj = null;
}
if (WaveOutDevice != null)
{
WaveOutDevice.Dispose();
WaveOutDevice = null;
}
}
public void Dispose()
{
if (_mediaCloserThread.IsAlive)
{
_mediaCloserThread.Abort();
}
if (_botThread.IsAlive)
{
_botThread.Abort();
}
}
}
public class MediaCloser
{
public void CheckForStoppedPlayback()
{
while (true)
{
// continuously check for our stopped playback state to cleanup
Thread.Sleep(500);
if (SoundboardSingleton.Instance.WaveOutDevice != null &&
SoundboardSingleton.Instance.WaveOutDevice.PlaybackState == PlaybackState.Stopped)
{
SoundboardSingleton.Instance.CloseWaveOut();
SoundboardSingleton.Instance.IsBusy = false;
}
}
}
}
public class StayAliveBot
{
public void Live()
{
while (true)
{
// prevent bot from going idle
Thread.Sleep(1500000);
if (!SoundboardSingleton.Instance.IsBusy)
{
SoundboardSingleton.Instance.PlaySound(ConfigurationManager.AppSettings["SoundboardHeartbeatFile"]);
}
}
}
}
Hopefully this helps anyone running into the same problems. My site has been up and running for a few hours now with no issues and clients spamming the board. Thanks again everyone who helped.

How to update GUI from FileWatcher event with Task-based Asynchronous Pattern (TAP)

Recently I started learning C# by writing a simple application that watches directory and updates form based on lines written to file in that directory. At some point I was stuck with common InvalidOperationException while trying to update form element from FileWatcher event.
I've searched stackoverflow, and it seems that I should use Task-based Asynchronous Pattern (TAP) in such situations, but I can't figure out which method I should flag as async, and which to start as a Task. There are many related questions on stackoverflow, but none I've found cover all 3 aspects of my application:
Using FileWatcher
Updating Form element
Using TAP
So, what is the best practice to update Form elements from events, fired by FileWatcher if I want to use Task-based Asynchronous Pattern? Or should I use another pattern / another application structure?
Here is a simplified example of my app:
// Form
public partial class FormMain : Form, IDisplayInterface
{
private CoreClass coreClass;
public void SetSomeVaue(string value)
{
label.Text = value;
}
public FormMain()
{
coreClass = new CoreClass();
coreClass.StartFileWatcher();
}
private void FormMain_Shown(object sender, EventArgs e)
{
coreClass.DisplayInterface = this;
}
}
// Interface
interface IDisplayInterface
{
void SetSomeVaue(string value);
}
// CoreClass
class CoreClass
{
public IDisplayInterface DisplayInterface;
public void StartFileWatcher()
{
FileSystemWatcher watcher = new FileSystemWatcher("C:\Some\Folder")
{
NotifyFilter = NotifyFilters.Size
};
watcher.Changed += new FileSystemEventHandler(FileUpdated);
watcher.EnableRaisingEvents = true;
}
private void FileUpdated(object source, FileSystemEventArgs e)
{
ParseFile(Path.Combine("C:\Some\Folder", e.Name));
}
private void ParseFile(string File)
{
// foreach (var line in newFileLines)
ParseNewRecord(line);
}
private void ParseNewRecord(string line)
{
if (someCondition && DisplayInterface != null)
{
// This triggers Exception for accessing FormMain from a thread that did not create it
DisplayInterface.SetSomeValue(someValue);
}
}
}
UPDATE 21.07:
It looks that I got the wrong idea about using TAP everywhere, so I finally did it by invoking a delegate containing my SetSomeVaue method and it works correctly (I hope that is a correct decision).
Thanks for response!

Connectivity method does not get invoked in xamarin forms / c#

I have a Xamarin Forms application that should fire a method to execute once it detects Internet Connectivity. I have used
CrossConnectivity.Current.ConnectivityChanged
to monitor internet connectivity changes which works well.
I then invoke the method in the constructor of the page along with other methods that need to be executed on page load. But when I changed the status of the internet connection, the method SendUntransferedData(); does not get executed. Hence all my untransferred data are not transferred. Can anyone please help me to resolve this issue or provide me an alternative way of doing this?
The following code in MainPage.cs are:
public MainPage() {
DoSomething();
SearchData();
SendUntransferedData();
}
The code is implemented in the cs also.
private void SendUntransferedData()
{
CrossConnectivity.Current.ConnectivityChanged += (sender, args) =>
{
DataAccess da = new DataAccess();
da.SendUntransferredData();
};
}
Step1:
First, you need to add these two packages
Xam.Plugin.Connectivity
plugin.wifiinfo
in all platforms
Step2:
Create one class in PCL.
NetworkCheck.cs
public static bool IsNetworkConnected ()
{
bool retVal = false;
try
{
retVal = CrossConnectivity.Current.IsConnected;
return retVal;
}
catch (Exception ex) {
System.Diagnostics.Debug.WriteLine (ex.Message);
throw ex;
}
}
Step3:
In your pages use the code below
if (CheckNetworkAccess.IsNetworkConnected ())
{
Navigation.PushAsync (new HomePage ());
}
else
{
await DisplayAlert ("Your app name", "Please check your network connection", "OK");
}
The alternative I use is the following. In my App.xaml.cs file I add the code to hook up the ConnectivityChanged. That way I have one spot in my app where this is handled and since App.xaml.cs is always around for the lifetime of my app it's an ideal spot to put this.
If hooks up to the events from CrossConnectivity and then uses the Xamarin Forms MessagingCenter to broadcast a message to all the subscribers.
protected override void OnStart()
{
// Handle when your app starts
OnResume();
}
protected override void OnResume()
{
// Handle when your app resumes
Settings.IsConnected = CrossConnectivity.Current.IsConnected;
CrossConnectivity.Current.ConnectivityChanged += ConnectivityChanged;
}
protected override void OnSleep()
{
// Handle when your app sleeps
CrossConnectivity.Current.ConnectivityChanged -= ConnectivityChanged;
}
protected async void ConnectivityChanged(object sender, ConnectivityChangedEventArgs e)
{
// Save current state and then set it
var connected = Settings.IsConnected;
Settings.IsConnected = e.IsConnected;
// If connectivity changes we inform our listeners of that.
if (connected != e.IsConnected)
MessagingService.Current.SendMessage(MessageKeys.ConnectivityChanged);
// If we disconnected, show a message for the user that we did.
if (connected && !e.IsConnected)
{
// We went offline, should alert the user and also update ui (done via settings)
var task = UserDialogs.Instance.AlertAsync("You are offline.", "Connection lost", "OK");
if (task != null)
await task;
}
}
I then create a property in a base viewmodel that all my other viewmodels inherit from. This can be bound to interface controls whenever necessary.
public bool IsConnected { get; set; }
In my viewmodel I then subscribe to the event that is being thrown in App.xaml.cs.
MessagingService.Current.Subscribe(MessageKeys.ConnectivityChanged, (c) =>
{
IsConnected = CrossConnectivity.Current.IsConnected;
// Additional things you want to do such as
// DataAccess da = new DataAccess();
// da.SendUntransferredData();
});
You could probably abstract this a bit further depending on the MVVM framework you use (if you use one) but I will not go into that since it's unrelated to the original question. This solution uses additional NuGet packages namely:
FormsToolkit which has a simplified version of the MessagingCenter
Settings plugin to store the IsConnected state.

Windows service does nothing on production PC

I have an application that schedules jobs using Quartz.Net. It works on my development laptop perfectly both as a winforms application (with start and stop buttons) and as a Windows Services whose OnStart() and OnStop() event code matches the start and stop button code of the winforms application. They're both in the same solution using the same "model" code (in its own project).
If I run the winforms application on the production computer it works perfectly, the jobs are executed according to their schedule as expected. However if I install and run it as a Windows Service on the production PC nothing happens! The jobs do not run.
I have no idea how to debug this. Please let me know if you have any suggestions as to what might be wrong.
Also please let me know what other information I should be providing.
Oh - dev PC is running Windows 7, production PC is running Windows 8.1! Could that be the problem? I built the service by following this tutorial: http://msdn.microsoft.com/en-us/library/zt39148a(v=vs.110).aspx which does not indicate that anything special needs to be done for deploying to Windows 8?
Could this have something to do with environment variables (which I know nothing about)?
Here is some code which may be relevant:
The service:
namespace DataPump
{
public partial class DataPumpService : ServiceBase
{
private TaskManager _taskManager;
public DataPumpService()
{
InitializeComponent();
_taskManager = new TaskManager();
}
protected override void OnStart(string[] args)
{
_taskManager.Go();
}
protected override void OnStop()
{
_taskManager.Stop();
}
}
}
The form code (different project):
namespace DataPump
{
public partial class Form1 : Form
{
private TaskManager _taskManager = new TaskManager();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
_taskManager.Go(); //Loops infinitely, does not block
label1.Text = "Running...";
}
private void button2_Click(object sender, EventArgs e)
{
label1.Text = "Stopping...";
_taskManager.Stop();
label1.Text = "Idle";
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
_taskManager.Stop();
}
}
}
Selected code from TaskManager code (third project which the first two each reference):
public class TaskManager
{
//...
private IScheduler _scheduler = StdSchedulerFactory.GetDefaultScheduler();
//...
public void Go()
{
if (_scheduler.GetCurrentlyExecutingJobs().Count() == 0)
{
_scheduler.Start();
_scheduler.AddCalendar(CalendarName, MakeSAPublicHolidayCalendar(), false, true);
foreach (DatapumpJob job in JobList)
{
_scheduler.ScheduleJob(MakeJob(job), MakeTriggerCron(job));
}
}
}
//...
public void Stop()
{
foreach (string name in _scheduler.GetCurrentlyExecutingJobs().Select(j => j.JobDetail.Key.Name))
{
_scheduler.Interrupt(new JobKey(name));
}
_scheduler.Shutdown(true);
}
//...
}
Where JobList is a get only property that generates a List<DatapumpJob>where DatapumpJob implements IInterrutableJob but adds common features including a job name which gets use by the three methods beginning Make... which are all private methods within the TaskManager class.
This code is to answer a question from the comments regarding ServiceBase.Run():
Program.cs (auto-generated):
namespace DataPump
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new DataPumpService()
};
ServiceBase.Run(ServicesToRun);
}
}
}
This turned out to be a network permissions issue. The service was running, it was just unable to access the network drive. So really my question was mi-specified.
After trying this: https://serverfault.com/questions/177139/windows-service-cant-access-network-share we eventually got it to work by setting the service to run as a specific user account on the PC.

Categories

Resources