Im having a problem installing my service application. When I'm running my debug mode it all works correctly and all the logical stuff works. I've written service applications before and comparing the two there is little difference between this one and a working one. Thanks in advance for any help on my code:
class MainClass : ServiceBase
{
ABCSQLCon _SQLCon = new ABCSQLCon();
private int cycleTime = 0;
private delegate void processError(String errorMessage);
static void Main(string[] args)
{
#if(!DEBUG)
ServiceBase.Run(new MainClass());
#else
MainClass service = new MainClass();
service.OnStart(new string[0]);
#endif
}
protected override void OnStart(string[] args)
{
addToLog("Testing SQL Connection...", "Log");
cycleTime = _SQLCon.sleepTime;
addToLog("Sleep Time has been set...", "Log");
if (_SQLCon.testSQLConnection())
{
addToLog("Connection to SQL Database succeeded", "Log");
// queryThread();
//not neccessary to make applicated multithreaded yet.
addToLog("Starting Query Thread...", "Log");
ThreadStart queryCycle = new ThreadStart(queryThread);
Thread qThread = new Thread(queryCycle);
qThread.Start();
}
}
private void startProgram()
{
}
protected override void OnStop()
{
base.OnStop();
}
public MainClass()
{
this.ServiceName = "ABCSQL Engine";
}
Ah I found the problem now, the sql connection test was just a quick open and close job but what I didn't see or realise was where I was initializing that _SQLCON object.
I've moved that to my method and works fine now. Happy days, thanks for the answers as it helped me look in the place I wasnt looking. x
It is a best practise to call to methods on a different thread then the service thread to avoid the blocking of the service thread
public void MyMethod()
{
addToLog("Testing SQL Connection...", "Log");
cycleTime = _SQLCon.sleepTime;
addToLog("Sleep Time has been set...", "Log");
if (_SQLCon.testSQLConnection())
{
addToLog("Connection to SQL Database succeeded", "Log");
// queryThread();
//not neccessary to make applicated multithreaded yet.
addToLog("Starting Query Thread...", "Log");
ThreadStart queryCycle = new ThreadStart(queryThread);
Thread qThread = new Thread(queryCycle);
qThread.Start();
}
}
private Thread _myThread;
protected override void OnStart(string[] args)
{
ThreadStart threadStart = MyMethod;
_myThread = new Thread(threadStart);
_myThread.Start();
}
The problem is that you are making the database connection in the initialisation of the service itself.
It work in debug because you're not actually starting it as a service:
#if(!DEBUG)
ServiceBase.Run(new MainClass());
#else
MainClass service = new MainClass();
service.OnStart(new string[0]);
#endif
I suspect that the start method is taking a long time because of this line:
if (_SQLCon.testSQLConnection())
You need to make sure that the start method of the service returns in a timely manner and any potentially long running process is done in a thread. If you move this test into a thread then you should find it working OK.
Related
I am working on a big ASP.Net 5 web application, and I would like to implement a self-update feature. Since the program can be deployed on many different platform and different ways (e.g. a Windows Service, a systemd service on linux, Docker container, etc...), the only viable way of implementing a self update mechanism is to write a host process that can load and unload the main program DLL and its dependencies. Unloading is important because the host program (updater) must be able to overwrite the server's DLL files while it's running.
Here's what I've done so far: I have changed the output type of the main web application to Library, and I've made a small loader program that loads this DLL and its dependencies into an HostAssemblyLoadContext, then invokes the original Main() method. The server application is programmed to shut down gracefully a few seconds after it starts up, so the CreateHostBuilder(args).Build().Run() and the Main() method can return. After this, I try to call HostAssemblyLoadContext.Unload(), but for some reason, the load context refuses to actually unload.
Here's my HostAssemblyLoadContext implementation:
public class HostAssemblyLoadContext : AssemblyLoadContext
{
private readonly AssemblyDependencyResolver _resolver;
public HostAssemblyLoadContext(string pluginPath) : base(isCollectible: true)
{
_resolver = new AssemblyDependencyResolver(pluginPath);
}
protected override Assembly? Load(AssemblyName name)
{
string? assemblyPath = _resolver.ResolveAssemblyToPath(name);
if (assemblyPath != null)
return LoadFromAssemblyPath(assemblyPath);
string filePath = $"{name.FullName.Split(',')[0]}.dll";
if(File.Exists(filePath))
return Assembly.LoadFrom(filePath);
return null;
}
}
My loader code:
[MethodImpl(MethodImplOptions.NoInlining)]
private static void ExecuteMainMethod(string[] args, string assemblyFileName, out WeakReference contextReference, out HostAssemblyLoadContext loadContext)
{
loadContext = new HostAssemblyLoadContext(new DirectoryInfo(".").FullName);
contextReference = new WeakReference(loadContext);
var assembly = loadContext.LoadFromAssemblyPath(assemblyFileName);
var mainMethod = FindMainMethod(assembly)!;
try
{
mainMethod.Invoke(null, new object?[] {args});
}
catch
{
// ignore
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void Unload(WeakReference weakReference, ref HostAssemblyLoadContext loadContext)
{
loadContext.Unload();
loadContext = null;
for (int i = 0; weakReference.IsAlive && i < 100; i++)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
Console.WriteLine($"is alive: {weakReference.IsAlive}");
}
public static void Main(string[] args)
{
ExecuteMainMethod(args, new FileInfo("FirestormSW.SmartGrade.dll").FullName, out var weakReference, out var loadContext);
Unload(weakReference, ref loadContext);
Console.Out.WriteLine("Press ENTER to exit");
Console.ReadLine();
}
And the main method of the actual ASP server:
public static void Main(string[] args)
{
Console.Out.WriteLine("Starting Server...");
CreateHostBuilder(args).Build().Run();
Console.Out.WriteLine("Server stopped.");
}
Note that if I replace the contents of this Main method with a simple Thread.Sleep(1000), the context does unload successfully.
When I run this program, and wait for the server to shut itself down, this is what I see on the console:
Starting Server...
Shutting down...
is alive: True
This means that the server's main method has returned, but something is still keeping the load context alive. I have looked at the thread count (Process.GetCurrentProcess().Threads.Count) before and after the server is started/stopped, and the number jumps from 8 up to 27. This makes me assume that the context is being kept alive by some threads that are created by the ASP.Net application, but I'm not sure. And if that's the case, I don't know how to find out which threads are responsible, and even if I could, I'm not sure if it's possible to abort them.
We have windows service which is running fine untill any exceptions occured in the process.
It contains two Threads (GenerateInvoice and GenerateReport).
These threads are getting blocked and results in DeadLock like situation mostly when there is high CPU usage on our DataBase server.
We have done some changes in code to handle such situations like added while condition below code but still it is not working.
Below is the OnStart() method of service:
protected override void OnStart(string[] args)
{
try
{
log.Debug("Starting Invoice Generation Service");
_thread = new Thread(new ThreadStart((new GenerateInvoice()).Process));
_thread.IsBackground = true;
_thread.Start();
_reportThread = new Thread(new ThreadStart((new GenerateReport()).Process));
_reportThread.IsBackground = true;
_reportThread.Start();
}
catch (Exception ex)
{
log.Error("Error in Invoice Generation Service:", ex);
}
}
Here is the processing code of first thread: GenerateInvoice
public void Process()
{
while (isProcessActive)
{
try
{
DBBilling obj = new DBBilling();
DataTable dtInvoiceID = obj.readData(#"SELECT * FROM (SELECT ird.BillByType, ird.InvoiceID, ir.BeginDate, ir.EndDate, ir.SendToQB, ir.SendEmail,
i.ARAccountID, i.ARAccountHotelID, i.invoiceNumber,i.[STATUS],UPDATETIME,row_number() over (PARTITION BY ird.INVOICEID ORDER BY UPDATETIME DESC) AS row_number
FROM Invoices i JOIN InvoicesRunRequestDetails ird ON ird.InvoiceID=i.InvoiceID
JOIN InvoicesRunRequest ir ON ird.RequestID = ir.RequestID
Where i.[STATUS] = 'PENDING') AS rows
WHERE ROW_NUMBER=1 ORDER BY UPDATETIME");
processCounter = 0;
#region process
if (dtInvoiceID != null && dtInvoiceID.Rows.Count > 0)
{
//some code here..
}
#endregion
}
catch (Exception ex) //Mantis 1486 : WEBPMS1 Disk Space : 10 Aug 2016
{
log.ErrorFormat("Generate Invoice -> Process -> InnLink Billing Execute Query Exception. Error={0}", ex);
if(DBBilling.dbConnTimeoutErrorMessage.Any(ex.Message.Contains))
{
processCounter++;
if (processCounter >= 1) //Need to change to 25 after Problem Solve
{
isProcessActive = false;
log.ErrorFormat("Generate Invoice -> Process -> RunInvoice Service exiting loop"); //From here control is not going back
}
else
System.Threading.Thread.Sleep(5000); //Sleep for 5 Sec
}
}
}
}
Processing of Second Thread i.e. GenerateReport code:
public void Process()
{
AppSettingsReader ar = new AppSettingsReader();
string constr = (string)ar.GetValue("BillingDB", typeof(string));
SqlConnection con = new SqlConnection(constr);
while (isProcessActive)
{
try
{
DBBilling obj = new DBBilling();
DataTable dtReportRunID = obj.readData(#"SELECT ReportRunID,MonYear, BeginDate, EndDate FROM ReportRunRequest
Where [STATUS] = 'PENDING' ORDER BY ReportRunID");
processCounter = 0;
if (dtReportRunID != null && dtReportRunID.Rows.Count > 0)
{
//some code here..
}
}
catch (Exception ex) //Mantis 1486 : WEBPMS1 Disk Space : 10 Aug 2016
{
log.ErrorFormat("Generate Report -> Process -> InnLink Billing Execute Query Exception. Error={0}", ex);
if (DBBilling.dbConnTimeoutErrorMessage.Any(ex.Message.Contains))
{
processCounter++;
if (processCounter >= 1) //Need to change to 25 after Problem Solve
{
isProcessActive = false;
log.ErrorFormat("Generate Report -> Process -> RunInvoice Service Exiting loop"); //From here control is not going back
}
else
System.Threading.Thread.Sleep(5000); //Sleep for 5 Sec
}
}
}
}
What possible solution to avoid such conditions?
The way to avoid it is to either lock every access to a global variable, or not to use global variables.
here is one obvious example
DBBilling.dbConnTimeoutErrorMessage.Any(ex.Message.Contains)
dbConnTimeoutErrorMessage is a static field that is being used from two different threads and I assume is not thread safe, surround access to it with a
lock(locObj)
{
// access to dbConnTimeoutErrorMessage
}
I am gonna go ahead and guess that log is also a global variable. Perhaps maybe even isProcessActive or processCounter.
I am guessing there is more in those comments - make sure your code is threadsafe before using it with two different threads.
I doubt locking access to what I said will fix your problem, but I guess your lack of threadsafe programming in these is a symptom to not using lock when it is needed. The secret is to lock every access to a global context, and just that.
What i suggest is to use Timer instead of infinite loop and as mentioned earlier in other answere you need some kind of synchronization. First of all, you need to implement your variables which used in different threads as follows (i don't know exactly definitions of your variables, but main idea is to use volatile keyword in your case):
public static volatile bool isProcessActive;
public static volatile int proccessCounter;
volatile keyword switches off the compiler optimizations for using variable in one thread. It means that your variables now are thread safe.
Next you need to use neither System.Threading.Timer or System.Timers.Timer. I will use in my example second one.
public sealed class GenerateInvoice :
{
protected const int timerInterval = 1000; // define here interval between ticks
protected Timer timer = new Timer(timerInterval); // creating timer
public GenerateInvoice()
{
timer.Elapsed += Timer_Elapsed;
}
public void Start()
{
timer.Start();
}
public void Stop()
{
timer.Stop();
}
public void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
try
{
DBBilling obj = new DBBilling();
DataTable dtInvoiceID = obj.readData(#"SELECT * FROM (SELECT ird.BillByType, ird.InvoiceID, ir.BeginDate, ir.EndDate, ir.SendToQB, ir.SendEmail,
i.ARAccountID, i.ARAccountHotelID, i.invoiceNumber,i.[STATUS],UPDATETIME,row_number() over (PARTITION BY ird.INVOICEID ORDER BY UPDATETIME DESC) AS row_number
FROM Invoices i JOIN InvoicesRunRequestDetails ird ON ird.InvoiceID=i.InvoiceID
JOIN InvoicesRunRequest ir ON ird.RequestID = ir.RequestID
Where i.[STATUS] = 'PENDING') AS rows
WHERE ROW_NUMBER=1 ORDER BY UPDATETIME");
processCounter = 0;
#region process
if (dtInvoiceID != null && dtInvoiceID.Rows.Count > 0)
{
//some code here..
}
#endregion
}
catch (Exception ex) //Mantis 1486 : WEBPMS1 Disk Space : 10 Aug 2016
{
log.ErrorFormat("Generate Invoice -> Process -> InnLink Billing Execute Query Exception. Error={0}", ex);
if(DBBilling.dbConnTimeoutErrorMessage.Any(ex.Message.Contains))
{
processCounter++;
if (processCounter >= 1) //Need to change to 25 after Problem Solve
{
isProcessActive = false;
// supposing that log is a reference type and one of the solutions can be using lock
// in that case only one thread at the moment will call log.ErrorFormat
// but better to make synchronization stuff unside logger
lock (log)
log.ErrorFormat("Generate Invoice -> Process -> RunInvoice Service exiting loop"); //From here control is not going back
}
else
// if you need here some kind of execution sleep
// here you can stop timer, change it interval and run again
// it's better than use Thread.Sleep
// System.Threading.Thread.Sleep(5000); //Sleep for 5 Sec
}
}
}
}
Use the same approach for the GenerateReport to make Timer-based.
And, finally, you need to change your OnStart and OnStop methods something like so:
protected GenerateInvoice generateInvoice;
protected GenerateReport generateReport;
protected override void OnStart(string[] args)
{
// all exception handling should be inside class
log.Debug("Starting Invoice Generation Service");
generateInvoice = new GenerateInvoice();
generateInvoice.Start();
generateReport = new GenerateReport();
generateReport.Start();
}
protected override void OnStop()
{
generateInvoice.Stop();
generateReport.Stop();
}
I am working on a windows service that should collect all incoming TCP/IP streams with this class. It gets started from the services OnStart() void.
However I have encountered that when messages come in from a second sender, the whole communication stops working. The service then does not react to a single connection until you restart it.
public class TCPIP
{
public static Receiver rc = new Receiver();
public class Receiver
{
public delegate void ReceivedEventHandler(string ReceivedText, string Sender);
public event ReceivedEventHandler Received;
public void Start()
{
System.Threading.Thread th = new System.Threading.Thread(internals);
th.Start();
}
private void internals()
{
TcpListener _listener = new TcpListener(1994);
_listener.Start();
while (true)
{
TcpClient rcclient = _listener.AcceptTcpClient();
StreamReader reader = new StreamReader(rcclient.GetStream());
string msg = "";
while (reader.Peek() > -1)
{
msg += Convert.ToChar(reader.Read()).ToString();
}
Received(msg, rcclient.Client.RemoteEndPoint.ToString().Split(Convert.ToChar(":"))[0]);
// Cleanup
rcclient.Close();
reader.Close();
}
}
Could anybody help me out improving this class to answer connections from multiple endpoints, and to not be occupied after one?
Thank you very much in advance.
Just create a thread when you accept some connection, so if it's blocked will not afect the main program.
Btw you can try AcceptTcpClientAsync to prevent blocking calls.
I have WCF classes and now project to do. My teacher gave as to write app with streaming and duplex (I know that is impossible, but I found backdoor from this situation - I'm sending pics under 60KB).
My code worked well so far as I start wrote my GUI in Windows Form Application.
When I'm testing it via console - everything work well. But, when I want to use buttons in my GUI i have this exception:
An unhandled exception of type 'System.TimeoutException' occurred in
mscorlib.dll Additional information: This request operation sent to
net.tcp://localhost:7756/Przesylanie did not receive a reply within
the configured timeout (00:00:59.9740007). The time allotted to this
operation may have been a portion of a longer timeout. This may be
because the service is still processing the operation or because the
service was unable to send a reply message. Please consider
increasing the operation timeout (by casting the channel/proxy to
IContextChannel and setting the OperationTimeout property) and ensure
that the service is able to connect to the client.
Here bunch of code:
Service + IService (due to limitation of Stack I put it to one file):
public void WyslijstrumienNaSerwer()
{
IPrzesylanieCallback callback = OperationContext.Current.GetCallbackChannel<IPrzesylanieCallback>();
string sciezka = #"E:\5 semestr\Fras\Se płotek\Lab6\WcfServiceContractZadanie2\Host\bin\Pliki\" + "plik_odebrany.jpg";
string filePath = Path.Combine(System.Environment.SystemDirectory, sciezka);
Console.WriteLine("start");
callback.WyslijStrumien(filePath);
Console.WriteLine(filePath);
Console.WriteLine("meta");
}
namespace WcfServiceContractZadanie2
{
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IPrzesylanieCallback))]
public interface IPrzesylanie
{
[OperationContract]
void WyslijstrumienNaSerwer();
}
[ServiceContract]
public interface IPrzesylanieCallback
{
[OperationContract(IsOneWay = true)]
void WyslijStrumien(string filePath);
}
}
Client + callback + form + References.cs:
namespace Aplikacja
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
PrzesylanieClient klient = new PrzesylanieClient(new InstanceContext(new PrzesylanieCallback()), "NetTcpBinding_IPrzesylanie");
klient.WyslijstrumienNaSerwer();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
public class PrzesylanieCallback : Referencja1.IPrzesylanieCallback
{
public void WyslijStrumien(string filePath)
{
Form1 o1 = new Form1();
// Pobieranie obrazka od klienta
string sciezka = #"E:\5 semestr\Fras\Se płotek\Lab6\WcfServiceContractZadanie2\Aplikacja\bin\Pliki\" + o1.wybrany();
string filePathZrodla = Path.Combine(System.Environment.SystemDirectory, sciezka);
//Otwieranie obrazka
Stream strumien = null;
try
{
FileStream imageFile = File.OpenRead(filePathZrodla);
strumien = (Stream)imageFile;
}
catch(IOException ioe)
{
Console.WriteLine("Wyjatek otwierania pliku: {0}", ioe.ToString());
Console.ReadKey();
throw ioe;
}
// Zapisywanie obrazka
o1.SaveFile(strumien, filePath);
}
}
}
private void btnPrzeslijPlikNaSerwer_Click(object sender, EventArgs e)
{
PrzesylanieClient klient = new PrzesylanieClient(new InstanceContext(new PrzesylanieCallback()), "NetTcpBinding_IPrzesylanie");
klient.WyslijstrumienNaSerwer();
}
public void WyslijstrumienNaSerwer() {
base.Channel.WyslijstrumienNaSerwer();
}
I wrote methods SaveFile which works correctly.
As you see, I'm testing my code in the begining of Main function in Client and that works well.
But when I'm using the same code in Forms it does not work. Compiler is returnig me to References.cs and gives me exception I mentioned earlier.
Waiting for any respone!
My answer does not solve your exception issue; however, it might prevent you from getting a very bad grade. Streaming AND duplex are both supported in WCF.
Streaming:
https://msdn.microsoft.com/en-us/library/ms733742(v=vs.110).aspx
Duplex:
https://msdn.microsoft.com/en-us/library/ms731064(v=vs.110).aspx
I have built a windows Service to monitor a few settings on our servers, I have developed quite a few WinForm and WPF apps but I am an absolute newbie when it comes to Windows Services, which is why I resorted to msdn and followed the tutorial on how to create a simple service. Now I can install the service just fine and make it run, but only if I cut some bits and pieces out of the microsoft tutorial.. but I am curious why, when I follow the tutorial, my service gets an unexpected error at startup.
After some testing it seems that the service seems to crash in the onstart method at SetServiceStatus()
public partial class MyService: ServiceBase
{
private static ManualResetEvent pause = new ManualResetEvent(false);
[DllImport("ADVAPI32.DLL", EntryPoint = "SetServiceStatus")]
public static extern bool SetServiceStatus(IntPtr hServiceStatus, SERVICE_STATUS lpServiceStatus);
private SERVICE_STATUS myServiceStatus;
private Thread workerThread = null;
public MyService()
{
InitializeComponent();
CanPauseAndContinue = true;
CanHandleSessionChangeEvent = true;
ServiceName = "MyService";
}
static void Main()
{
// Load the service into memory.
System.ServiceProcess.ServiceBase.Run(MyService());
}
protected override void OnStart(string[] args)
{
IntPtr handle = this.ServiceHandle;
myServiceStatus.currentState = (int)State.SERVICE_START_PENDING;
**SetServiceStatus(handle, myServiceStatus);**
// Start a separate thread that does the actual work.
if ((workerThread == null) || ((workerThread.ThreadState & (System.Threading.ThreadState.Unstarted | System.Threading.ThreadState.Stopped)) != 0))
{
workerThread = new Thread(new ThreadStart(ServiceWorkerMethod));
workerThread.Start();
}
myServiceStatus.currentState = (int)State.SERVICE_RUNNING;
SetServiceStatus(handle, myServiceStatus);
}
}
Now my service seems to run just fine when I comment out the SetServiceStatus() lines. Why does this fail? Is this a rights-issue or am I completely missing the point here?
In general, you shouldn't have to call SetServiceStatus when implementing a managed service using the framework.
That being said, if you do call it, you need to fully initialize the SERVICE_STATUS before using it. You're currently only setting the state, but none of the other variables.
This is suggested in the best practices for SetServiceStatus: "Initialize all fields in the SERVICE_STATUS structure, ensuring that there are valid check-point and wait hint values for pending states. Use reasonable wait hints."