I am currently writing a little bootstrap code for a service that can be run in the console. It essentially boils down to calling the OnStart() method instead of using the ServiceBase to start and stop the service (because it doesn't run the application if it isn't installed as a service and makes debugging a nightmare).
Right now I am using Debugger.IsAttached to determine if I should use ServiceBase.Run or [service].OnStart, but I know that isn't the best idea because some times end users want to run the service in a console (to see the output etc. realtime).
Any ideas on how I could determine if the Windows service controller started 'me', or if the user started 'me' in the console? Apparantly Environment.IsUserInteractive is not the answer. I thought about using commandline args, but that seems 'dirty'.
I could always see about a try-catch statement around ServiceBase.Run, but that seems dirty. Edit: Try catch doesn't work.
I have a solution: putting it up here for all the other interested stackers:
public void Run()
{
if (Debugger.IsAttached || Environment.GetCommandLineArgs().Contains<string>("-console"))
{
RunAllServices();
}
else
{
try
{
string temp = Console.Title;
ServiceBase.Run((ServiceBase[])ComponentsToRun);
}
catch
{
RunAllServices();
}
}
} // void Run
private void RunAllServices()
{
foreach (ConsoleService component in ComponentsToRun)
{
component.Start();
}
WaitForCTRLC();
foreach (ConsoleService component in ComponentsToRun)
{
component.Stop();
}
}
EDIT: There was another question on StackOverflow where the guy had problems with the Environment.CurrentDirectory being "C:\Windows\System32" looks like that may be the answer. I will test today.
Another workaround.. so can run as WinForm or as windows service
var backend = new Backend();
if (Environment.UserInteractive)
{
backend.OnStart();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Fronend(backend));
backend.OnStop();
}
else
{
var ServicesToRun = new ServiceBase[] {backend};
ServiceBase.Run(ServicesToRun);
}
I usually flag my Windows service as a console application which takes a command line parameter of "-console" to run using a console, otherwise it runs as a service. To debug you just set the command line parameters in the project options to "-console" and you're off!
This makes debugging nice and easy and means that the app functions as a service by default, which is what you'll want.
What works for me:
The class doing the actual service work is running in a separate thread.
This thread is started from within the OnStart() method, and stopped from OnStop().
The decision between service and console mode depends on Environment.UserInteractive
Sample code:
class MyService : ServiceBase
{
private static void Main()
{
if (Environment.UserInteractive)
{
startWorkerThread();
Console.WriteLine ("====== Press ENTER to stop threads ======");
Console.ReadLine();
stopWorkerThread() ;
Console.WriteLine ("====== Press ENTER to quit ======");
Console.ReadLine();
}
else
{
Run (this) ;
}
}
protected override void OnStart(string[] args)
{
startWorkerThread();
}
protected override void OnStop()
{
stopWorkerThread() ;
}
}
Like Ash, I write all actual processing code in a separate class library assembly, which was then referenced by the windows service executable, as well as a console app.
However, there are occasions when it is useful to know if the class library is running in the context of the service executable or the console app. The way I do this is to reflect on the base class of the hosting app. (Sorry for the VB, but I imagine that the following could be c#-ified fairly easily):
Public Class ExecutionContext
''' <summary>
''' Gets a value indicating whether the application is a windows service.
''' </summary>
''' <value>
''' <c>true</c> if this instance is service; otherwise, <c>false</c>.
''' </value>
Public Shared ReadOnly Property IsService() As Boolean
Get
' Determining whether or not the host application is a service is
' an expensive operation (it uses reflection), so we cache the
' result of the first call to this method so that we don't have to
' recalculate it every call.
' If we have not already determined whether or not the application
' is running as a service...
If IsNothing(_isService) Then
' Get details of the host assembly.
Dim entryAssembly As Reflection.Assembly = Reflection.Assembly.GetEntryAssembly
' Get the method that was called to enter the host assembly.
Dim entryPoint As System.Reflection.MethodInfo = entryAssembly.EntryPoint
' If the base type of the host assembly inherits from the
' "ServiceBase" class, it must be a windows service. We store
' the result ready for the next caller of this method.
_isService = (entryPoint.ReflectedType.BaseType.FullName = "System.ServiceProcess.ServiceBase")
End If
' Return the cached result.
Return CBool(_isService)
End Get
End Property
Private Shared _isService As Nullable(Of Boolean) = Nothing
#End Region
End Class
Jonathan, not exactly an answer to your question, but I've just finished writing a windows service and also noted the difficulty with debugging and testing.
Solved it by simply writing all actual processing code in a separate class library assembly, which was then referenced by the windows service executable, as well as a console app and a test harness.
Apart from basic timer logic, all more complex processing happened in the common assembly and could be tested/run on demand incredibly easily.
I have modified the ProjectInstaller to append the command-line argument parameter /service, when it is being installed as service:
static class Program
{
static void Main(string[] args)
{
if (Array.Exists(args, delegate(string arg) { return arg == "/install"; }))
{
System.Configuration.Install.TransactedInstaller ti = null;
ti = new System.Configuration.Install.TransactedInstaller();
ti.Installers.Add(new ProjectInstaller());
ti.Context = new System.Configuration.Install.InstallContext("", null);
string path = System.Reflection.Assembly.GetExecutingAssembly().Location;
ti.Context.Parameters["assemblypath"] = path;
ti.Install(new System.Collections.Hashtable());
return;
}
if (Array.Exists(args, delegate(string arg) { return arg == "/uninstall"; }))
{
System.Configuration.Install.TransactedInstaller ti = null;
ti = new System.Configuration.Install.TransactedInstaller();
ti.Installers.Add(new ProjectInstaller());
ti.Context = new System.Configuration.Install.InstallContext("", null);
string path = System.Reflection.Assembly.GetExecutingAssembly().Location;
ti.Context.Parameters["assemblypath"] = path;
ti.Uninstall(null);
return;
}
if (Array.Exists(args, delegate(string arg) { return arg == "/service"; }))
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[] { new MyService() };
ServiceBase.Run(ServicesToRun);
}
else
{
Console.ReadKey();
}
}
}
The ProjectInstaller.cs is then modified to override a OnBeforeInstall() and OnBeforeUninstall()
[RunInstaller(true)]
public partial class ProjectInstaller : Installer
{
public ProjectInstaller()
{
InitializeComponent();
}
protected virtual string AppendPathParameter(string path, string parameter)
{
if (path.Length > 0 && path[0] != '"')
{
path = "\"" + path + "\"";
}
path += " " + parameter;
return path;
}
protected override void OnBeforeInstall(System.Collections.IDictionary savedState)
{
Context.Parameters["assemblypath"] = AppendPathParameter(Context.Parameters["assemblypath"], "/service");
base.OnBeforeInstall(savedState);
}
protected override void OnBeforeUninstall(System.Collections.IDictionary savedState)
{
Context.Parameters["assemblypath"] = AppendPathParameter(Context.Parameters["assemblypath"], "/service");
base.OnBeforeUninstall(savedState);
}
}
This thread is really old, but I thought I would throw my solution out there. Quite simply, to handle this type of situation, I built a "service harness" that is used in both the console and Windows service cases. As above, most of the logic is contained in a separate library, but this is more for testing and "linkability".
The attached code by no means represents the "best possible" way to solve this, just my own approach. Here, the service harness is called by the console app when in "console mode" and by the same application's "start service" logic when it is running as a service. By doing it this way, you can now call
ServiceHost.Instance.RunningAsAService (Boolean)
from anywhere in your code to check if the application is running as a service or simply as a console.
Here is the code:
public class ServiceHost
{
private static Logger log = LogManager.GetLogger(typeof(ServiceHost).Name);
private static ServiceHost mInstance = null;
private static object mSyncRoot = new object();
#region Singleton and Static Properties
public static ServiceHost Instance
{
get
{
if (mInstance == null)
{
lock (mSyncRoot)
{
if (mInstance == null)
{
mInstance = new ServiceHost();
}
}
}
return (mInstance);
}
}
public static Logger Log
{
get { return log; }
}
public static void Close()
{
lock (mSyncRoot)
{
if (mInstance.mEngine != null)
mInstance.mEngine.Dispose();
}
}
#endregion
private ReconciliationEngine mEngine;
private ServiceBase windowsServiceHost;
private UnhandledExceptionEventHandler threadExceptionHanlder = new UnhandledExceptionEventHandler(ThreadExceptionHandler);
public bool HostHealthy { get; private set; }
public bool RunningAsService {get; private set;}
private ServiceHost()
{
HostHealthy = false;
RunningAsService = false;
AppDomain.CurrentDomain.UnhandledException += threadExceptionHandler;
try
{
mEngine = new ReconciliationEngine();
HostHealthy = true;
}
catch (Exception ex)
{
log.FatalException("Could not initialize components.", ex);
}
}
public void StartService()
{
if (!HostHealthy)
throw new ApplicationException("Did not initialize components.");
try
{
mEngine.Start();
}
catch (Exception ex)
{
log.FatalException("Could not start service components.", ex);
HostHealthy = false;
}
}
public void StartService(ServiceBase serviceHost)
{
if (!HostHealthy)
throw new ApplicationException("Did not initialize components.");
if (serviceHost == null)
throw new ArgumentNullException("serviceHost");
windowsServiceHost = serviceHost;
RunningAsService = true;
try
{
mEngine.Start();
}
catch (Exception ex)
{
log.FatalException("Could not start service components.", ex);
HostHealthy = false;
}
}
public void RestartService()
{
if (!HostHealthy)
throw new ApplicationException("Did not initialize components.");
try
{
log.Info("Stopping service components...");
mEngine.Stop();
mEngine.Dispose();
log.Info("Starting service components...");
mEngine = new ReconciliationEngine();
mEngine.Start();
}
catch (Exception ex)
{
log.FatalException("Could not restart components.", ex);
HostHealthy = false;
}
}
public void StopService()
{
try
{
if (mEngine != null)
mEngine.Stop();
}
catch (Exception ex)
{
log.FatalException("Error stopping components.", ex);
HostHealthy = false;
}
finally
{
if (windowsServiceHost != null)
windowsServiceHost.Stop();
if (RunningAsService)
{
AppDomain.CurrentDomain.UnhandledException -= threadExceptionHanlder;
}
}
}
private void HandleExceptionBasedOnExecution(object ex)
{
if (RunningAsService)
{
windowsServiceHost.Stop();
}
else
{
throw (Exception)ex;
}
}
protected static void ThreadExceptionHandler(object sender, UnhandledExceptionEventArgs e)
{
log.FatalException("Unexpected error occurred. System is shutting down.", (Exception)e.ExceptionObject);
ServiceHost.Instance.HandleExceptionBasedOnExecution((Exception)e.ExceptionObject);
}
}
All you need to do here is replace that ominous looking ReconcilationEngine reference with whatever method is boostrapping your logic. Then in your application, use the ServiceHost.Instance.Start() and ServiceHost.Instance.Stop() methods whether you are running in console mode or as a service.
Maybe checking if the process parent is C:\Windows\system32\services.exe.
The only way I've found to achieve this, is to check if a console is attached to the process in the first place, by accessing any Console object property (e.g. Title) inside a try/catch block.
If the service is started by the SCM, there is no console, and accessing the property will throw a System.IO.IOError.
However, since this feels a bit too much like relying on an implementation-specific detail (what if the SCM on some platforms or someday decides to provide a console to the processes it starts?), I always use a command line switch (-console) in production apps...
Here is a translation of chksr's answer to .NET, and avoiding the bug that fails to recognize interactive services:
using System.Security.Principal;
var wi = WindowsIdentity.GetCurrent();
var wp = new WindowsPrincipal(wi);
var serviceSid = new SecurityIdentifier(WellKnownSidType.ServiceSid, null);
var localSystemSid = new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null);
var interactiveSid = new SecurityIdentifier(WellKnownSidType.InteractiveSid, null);
// maybe check LocalServiceSid, and NetworkServiceSid also
bool isServiceRunningAsUser = wp.IsInRole(serviceSid);
bool isSystem = wp.IsInRole(localSystemSid);
bool isInteractive = wp.IsInRole(interactiveSid);
bool isAnyService = isServiceRunningAsUser || isSystem || !isInteractive;
This is a bit of a self-plug, but I've got a little app that will load up your service types in your app via reflection and execute them that way. I include the source code, so you could change it slightly to display standard output.
No code changes needed to use this solution. I have a Debugger.IsAttached type of solution as well that is generic enough to be used with any service. Link is in this article:
.NET Windows Service Runner
Well there's some very old code (about 20 years or so, not from me but found in the wild, wild web, and in C not C#) that should give you an idea how to do the job:
enum enEnvironmentType
{
ENVTYPE_UNKNOWN,
ENVTYPE_STANDARD,
ENVTYPE_SERVICE_WITH_INTERACTION,
ENVTYPE_SERVICE_WITHOUT_INTERACTION,
ENVTYPE_IIS_ASP,
};
enEnvironmentType GetEnvironmentType(void)
{
HANDLE hProcessToken = NULL;
DWORD groupLength = 300;
PTOKEN_GROUPS groupInfo = NULL;
SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY;
PSID pInteractiveSid = NULL;
PSID pServiceSid = NULL;
DWORD dwRet = NO_ERROR;
DWORD ndx;
BOOL m_isInteractive = FALSE;
BOOL m_isService = FALSE;
// open the token
if (!::OpenProcessToken(::GetCurrentProcess(),TOKEN_QUERY,&hProcessToken))
{
dwRet = ::GetLastError();
goto closedown;
}
// allocate a buffer of default size
groupInfo = (PTOKEN_GROUPS)::LocalAlloc(0, groupLength);
if (groupInfo == NULL)
{
dwRet = ::GetLastError();
goto closedown;
}
// try to get the info
if (!::GetTokenInformation(hProcessToken, TokenGroups,
groupInfo, groupLength, &groupLength))
{
// if buffer was too small, allocate to proper size, otherwise error
if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
dwRet = ::GetLastError();
goto closedown;
}
::LocalFree(groupInfo);
groupInfo = (PTOKEN_GROUPS)::LocalAlloc(0, groupLength);
if (groupInfo == NULL)
{
dwRet = ::GetLastError();
goto closedown;
}
if (!GetTokenInformation(hProcessToken, TokenGroups,
groupInfo, groupLength, &groupLength))
{
dwRet = ::GetLastError();
goto closedown;
}
}
//
// We now know the groups associated with this token. We want
// to look to see if the interactive group is active in the
// token, and if so, we know that this is an interactive process.
//
// We also look for the "service" SID, and if it's present,
// we know we're a service.
//
// The service SID will be present iff the service is running in a
// user account (and was invoked by the service controller).
//
// create comparison sids
if (!AllocateAndInitializeSid(&siaNt,
1,
SECURITY_INTERACTIVE_RID,
0, 0, 0, 0, 0, 0, 0,
&pInteractiveSid))
{
dwRet = ::GetLastError();
goto closedown;
}
if (!AllocateAndInitializeSid(&siaNt,
1,
SECURITY_SERVICE_RID,
0, 0, 0, 0, 0, 0, 0,
&pServiceSid))
{
dwRet = ::GetLastError();
goto closedown;
}
// try to match sids
for (ndx = 0; ndx < groupInfo->GroupCount ; ndx += 1)
{
SID_AND_ATTRIBUTES sanda = groupInfo->Groups[ndx];
PSID pSid = sanda.Sid;
//
// Check to see if the group we're looking at is one of
// the two groups we're interested in.
//
if (::EqualSid(pSid, pInteractiveSid))
{
//
// This process has the Interactive SID in its
// token. This means that the process is running as
// a console process
//
m_isInteractive = TRUE;
m_isService = FALSE;
break;
}
else if (::EqualSid(pSid, pServiceSid))
{
//
// This process has the Service SID in its
// token. This means that the process is running as
// a service running in a user account ( not local system ).
//
m_isService = TRUE;
m_isInteractive = FALSE;
break;
}
}
if ( !( m_isService || m_isInteractive ) )
{
//
// Neither Interactive or Service was present in the current
// users token, This implies that the process is running as
// a service, most likely running as LocalSystem.
//
m_isService = TRUE;
}
closedown:
if ( pServiceSid )
::FreeSid( pServiceSid );
if ( pInteractiveSid )
::FreeSid( pInteractiveSid );
if ( groupInfo )
::LocalFree( groupInfo );
if ( hProcessToken )
::CloseHandle( hProcessToken );
if (dwRet == NO_ERROR)
{
if (m_isService)
return(m_isInteractive ? ENVTYPE_SERVICE_WITH_INTERACTION : ENVTYPE_SERVICE_WITHOUT_INTERACTION);
return(ENVTYPE_STANDARD);
}
else
return(ENVTYPE_UNKNOWN);
}
Seems I am bit late to the party, but interesting difference when run as a service is that at start current folder points to system directory (C:\windows\system32 by default). Its hardly unlikely user app will start from the system folder in any real life situation.
So, I use following trick (c#):
protected static bool IsRunAsService()
{
string CurDir = Directory.GetCurrentDirectory();
if (CurDir.Equals(Environment.SystemDirectory, StringComparison.CurrentCultureIgnoreCase))
{
return true;
}
return (false);
}
For future extension, additional check make be done for System.Environment.UserInteractive == false (but I do not know how it correlates with 'Allow service to interact with desktop' service settings).
You may also check window session by System.Diagnostics.Process.GetCurrentProcess().SessionId == 0 (I do not know how it correlates with 'Allow service to interact with desktop' service settings as well).
If you write portable code (say, with .NetCore) you may also check Environment.OSVersion.Platform to ensure that you are on windows first.
Related
This is my first project using the NetMQ (ZMQ) framework, so, maybe I didn't understand how to use it exactly.
I create a Windows Forms project with two applications, one send a "ping" to the other and receives a "pong" as an answer. The protocol is not so complex and uses the Request-Reply pattern, all the commands have a first part that identifies the objective, like "query" or "inform", and a second part that contains the command or the message itself. In this case one app send a "query-ping" and the other answer with "inform-pong".
I create a class to encapsulate all the dirty job, so the main form can use the protocol in a very simple way. Everything is working fine, but when I try to close the app, it gets stuck in the poller and the app never closes. In Visual Studio I can see the pause and stop button but I don't get any exception or errors:
and when I click in pause button I get this message (The application is in break mode):
If I click in 'Continue execution' the app back to same state and never closes.
If I remove the poller the app closes normally, but of course, the poller doesn't work and the app doesn't answer anymore.
This is the code from the Form1:
using CommomLib;
using System;
using System.Windows.Forms;
namespace Test1
{
public partial class Form1 : Form
{
ZmqCommunication zmqComm = new ZmqCommunication();
int portNumber;
string status;
public Form1()
{
InitializeComponent();
InitializeZmqComm();
}
public void InitializeZmqComm()
{
// Calls the ZMQ initialization.
portNumber = zmqComm.InitializeComm();
if (portNumber == 0)
status = "Ini error";
else
status = "Ini ok";
}
// Executes a ping command.
private void button1_Click(object sender, EventArgs e)
{
richTextBox1.Clear();
richTextBox1.AppendText(zmqComm.RequestPing(55001) + "\n");
}
}
}
And this is the code from my NetMQ class. It is in a separated library project. In my Dispose method I tried all combinations of Remove, Dispose and StopAsync and nothing works:
using NetMQ;
using NetMQ.Sockets;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
namespace CommomLib
{
public class ZmqCommunication
{
ResponseSocket serverComm = new ResponseSocket();
NetMQPoller serverPoller;
int _portNumber;
public ZmqCommunication()
{
_portNumber = 55000;
}
// Problem here! The serverPoller gets stuck.
public void Dispose()
{
//serverPoller.RemoveAndDispose(serverComm);
//serverComm.Dispose();
if (serverPoller.IsDisposed)
Debug.WriteLine("A");
//serverPoller.RemoveAndDispose(serverComm);
serverPoller.Remove(serverComm);
//serverPoller.StopAsync();
serverPoller.Dispose();
serverComm.Dispose();
if (serverPoller.IsDisposed)
Debug.WriteLine("B");
Thread.Sleep(500);
if (serverPoller.IsDisposed)
Debug.WriteLine("C");
Thread.Sleep(500);
if (serverPoller.IsDisposed)
Debug.WriteLine("D");
}
// ZMQ initialization.
public int InitializeComm()
{
bool ok = true;
bool tryAgain = true;
// Looks for a port.
while (tryAgain && ok)
{
try
{
serverComm.Bind("tcp://127.0.0.1:" + _portNumber);
tryAgain = false;
}
catch (NetMQ.AddressAlreadyInUseException)
{
_portNumber++;
tryAgain = true;
}
catch
{
ok = false;
}
}
if (!ok)
return 0; // Error.
// Set up the pooler.
serverPoller = new NetMQPoller { serverComm };
serverComm.ReceiveReady += (s, a) =>
{
RequestInterpreter();
};
// start polling (on this thread)
serverPoller.RunAsync();
return _portNumber;
}
// Message interpreter.
private void RequestInterpreter()
{
List<string> message = new List<string>();
if (serverComm.TryReceiveMultipartStrings(ref message, 2))
{
if (message[0].Contains("query"))
{
// Received the command "ping" and answers with a "pong".
if (message[1].Contains("ping"))
{
serverComm.SendMoreFrame("inform").SendFrame("pong");
}
}
}
}
// Send the command "ping".
public string RequestPing(int port)
{
using (var requester = new RequestSocket())
{
Debug.WriteLine("Running request port {0}", port);
requester.Connect("tcp://127.0.0.1:" + port);
List<string> msgResp = new List<string>();
requester.SendMoreFrame("query").SendFrame("ping");
if (requester.TryReceiveMultipartStrings(new TimeSpan(0, 0, 10), ref msgResp, 2))
{
if (msgResp[0].Contains("inform"))
{
return msgResp[1];
}
}
}
return "error";
}
}
}
Can you try to call NetMQConfig.Cleanup(); in window close event?
Place a breakpoint and see if you even get to ZmqCommunication.Dispose - that might be the issue - the form class not disposing the ZmqCommunication class.
Thanks guys for the answers, they were not the solution but pointed me in the right direction.
After a lot of debugging, I figured out it was a silly problem. I have two programs almost identical (I had both opened at the same time), one of them had the method Dispose() and the other no. During the debugging, in the breaking point, I thought I was in one program but I was in the other one. Really silly.
I have a delegate method to run a heavy process in my app (I must use MS Framework 3.5):
private delegate void delRunJob(string strBox, string strJob);
Execution:
private void run()
{
string strBox = "G4P";
string strJob = "Test";
delRunJob delegateRunJob = new delRunJob(runJobThread);
delegateRunJob.Invoke(strBox, strJob);
}
In some part of the method runJobThread
I call to an external program (SAP - Remote Function Calls) to retrieve data. The execution of that line can take 1-30 mins.
private void runJobThread(string strBox, string strJob)
{
// CODE ...
sapLocFunction.Call(); // When this line is running I cannot cancel the process
// CODE ...
}
I want to allow the user cancel whole process.
How can achieve this? I tried some methods; but I fall in the same point; when this specific line is running I cannot stop the process.
Instead of using the delegate mechanism you have to study the async and await mechanism. When you understand this mechanism you can move to cancellationtoken.
An example doing both things can be found here :
http://blogs.msdn.com/b/dotnet/archive/2012/06/06/async-in-4-5-enabling-progress-and-cancellation-in-async-apis.aspx
Well; I find out a complicated, but effective, way to solve my problem:
a.) I created a "Helper application" to show a notification icon when the process is running (To ensure to don't interfere with the normal execution of the main app):
private void callHelper(bool blnClose = false)
{
if (blnClose)
fw.processKill("SDM Helper");
else
Process.Start(fw.appGetPath + "SDM Helper.exe");
}
b.) I created a Thread that call only the heavy process line.
c.) While the Thread is alive I check for external file named "cancel" (The "Helper application" do that; when the user click an option to cancel the process the Helper create the file).
d.) If exists the file; dispose all objects and break the while cycle.
e.) The method sapLocFunction.Call() will raise an exception but I expect errors.
private void runJobThread(string strBox, string strJob)
{
// CODE ...
Thread thrSapCall = new Thread(() =>
{
try { sapLocFunction.Call(); }
catch { /* Do nothing */ }
});
thrSapCall.Start();
while (thrSapCall.IsAlive)
{
Thread.Sleep(1000);
try
{
if (fw.fileExists(fw.appGetPath + "\\cancel"))
{
sapLocFunction = null;
sapLocTable = null;
sapConn.Logoff();
sapConn = null;
canceled = true;
break;
}
}
finally { /* Do nothing */ }
}
thrSapCall = null;
// CODE ...
}
Works like a charm!
I think you would have to resort to the method described here. Read the post to see why this is a long way from ideal.
Perhaps this might work...
private void runJobThread(string strBox, string strJob, CancellationToken token)
{
Thread t = Thread.CurrentThread;
using (token.Register(t.Abort))
{
// CODE ...
sapLocFunction.Call(); // When this line is running I cannot cancel the process
// CODE ...
}
}
A bit of dnspy exposes a cancel method on nco3.0.
private readonly static Type RfcConnection = typeof(RfcSessionManager).Assembly.GetType("SAP.Middleware.Connector.RfcConnection");
private readonly static Func<RfcDestination, object> GetConnection = typeof(RfcSessionManager).GetMethod("GetConnection", BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate(typeof(Func<RfcDestination, object>)) as Func<RfcDestination, object>;
private readonly static MethodInfo Cancel = RfcConnection.GetMethod("Cancel", BindingFlags.Instance | BindingFlags.NonPublic);
object connection = null;
var completed = true;
using (var task = Task.Run(() => { connection = GetConnection(destination); rfcFunction.Invoke(destination); }))
{
try
{
completed = task.Wait(TimeSpan.FromSeconds(invokeTimeout));
if (!completed)
Cancel.Invoke(connection, null);
task.Wait();
}
catch(AggregateException e)
{
if (e.InnerException is RfcCommunicationCanceledException && !completed)
throw new TimeoutException($"SAP FM {functionName} on {destination} did not respond in {timeout} seconds.");
throw;
}
}
We have a WPF application, which uses WindowsFormsApplicationBase class to make it singleton; and is deployed using ClickOne. Whenever we wanted to execute this exe, we invoke it through Uri (deployed directory in server with query string). All works fine only for first instance of this application.
Problem: ClickOnce always passes the first Uri every time the single-instance application is activated regardless of what Uri is being passed. In addition, the StartupNextInstanceEventArgs is not populated for any subsequent instantiation of same application.
Has anyone had this issue?
Thanks in advance.
Well so far I haven't got any concrete answer for my question. Therefore, I decided implement a different solution which is mentioned here. Unfortunately, the original issue hold true in both approach. So i decided to do a workaround on top of the second approach (see the url) to solve this until I have a clean solution.
Workaround
Modified entry point (Main) to include a functionality, which saves the incoming activation uri to app config file. You must keep this position at which you save the value since it tended to override with old activation uri somewhere down the line. Remember this is my issue.
public static void Main()
{
string uri;
StartupHelpers.SetConfigurationValue("ActivationUri", (StartupHelpers.HasTriggeredFromUrl(out uri)) ? uri : string.Empty);
if (SingleInstance<App>.InitializeAsFirstInstance(Unique))
{
var application = new App();
application.Run();
SingleInstance<App>.Cleanup();
}
}
Now implement the interface (ISingletonInstanceApp) as below.
public bool SignalExternalCommandLineArgs(IList<string> args)
{
var uri = new Uri(StartupHelpers.GetConfigurationValue("ActivationUri"));
int queryString = 0;
if (StartupHelpers.IsTriggeredFromWLink(uri, out queryString))
{
//in my case I have a function LoadPage which take
//some parameter to populate UI. Your case might be
//totally different. However, the idea is on how we
//could grab running instance and pass value into
// it to do something different.
((YourMainWindow) (Current.MainWindow)).LoadPage(queryString.ToString());
}
// Bring window to foreground
if (this.MainWindow.WindowState == WindowState.Minimized)
{
this.MainWindow.WindowState = WindowState.Normal;
}
this.MainWindow.Activate();
return true;
}
Helper to get/set config values.
public static class StartupHelpers
{
public static bool HasTriggeredFromUrl(out string uri)
{
try
{
uri = string.Empty;
var activeUri = ApplicationDeployment.CurrentDeployment.ActivationUri;
uri = activeUri != null ? activeUri.ToString() : string.Empty;
return true;
}
catch (InvalidDeploymentException inv)
{
uri = string.Empty;
return false;
}
}
public static bool IsTriggeredFromLink(Uri activationUri, out int queryStringValue)
{
queryStringValue = 0;
var hasTriggeredFromLink = true;
if (string.IsNullOrWhiteSpace(activationUri.Query) ||
HttpUtility.ParseQueryString(activationUri.Query).Count <= 0)
hasTriggeredFromLink = false;
else
{
if (!int.TryParse(HttpUtility.ParseQueryString(activationUri.Query)[0], out queryStringValue))
throw new Exception("Invalid startup argument found from web site.");
}
return hasTriggeredFromLink;
}
public static bool SetConfigurationValue(string key, string value)
{
try
{
Configuration appConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
appConfig.AppSettings.Settings[key].Value = value;
appConfig.Save(ConfigurationSaveMode.Full);
ConfigurationManager.RefreshSection("appSettings");
}
catch (Exception ex)
{
throw ex;
}
return true;
}
public static string GetConfigurationValue(string key)
{
try
{
Configuration appConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ConfigurationManager.RefreshSection("appSettings");
return appConfig.AppSettings.Settings[key].Value;
}
catch (Exception ex)
{
throw ex;
}
}
}
A bit of a late answer but I happened to stumble upon this while looking into forcing an application to a single instance.
If I understand what you are trying to do, you want a single instance app which takes command line parameters. The second time the app is attempted to be run, you just want to keep the first instance but pass the second set of command line parameters to it.
In this case, why not host a WCF service in your WPF application with an appropriate method which is what you pass these parameters to instead. The web page that is utilizing the ClickOnce then just calls this service method with the parameters it would have passed.
Apologies for my English, I'm not a native speaker.
I'm trying to make a Windows service. If I try to build, install and run a VS template I don't get any errors.
I have ported my winform application to a service, made an installer, added some data sources, added a reference for the webservice, added some classes, but DIDN'T add any code to OnStart() and OnStop(). My code builds correctly and I can start and stop the service from the service manager.
However, if I add some code to the service class (which I don't call anywhere) and if I don't add code to OnStart() and to OnStop() then I can't start the service and the error is something like "The service doesn't respond the control functions". In the event log I can see the exception:
System.ArgumentOutOfRangeException
Stack:
in System.String.InternalSubStringWithChecks(Int32, Int32, Boolean)
in System.String.Substring(Int32, Int32)
in UpdaterFIAS.FIASMainClass.getNameFile(System.String, System.String, System.String)
in UpdaterFIAS.FIASMainClass..ctor()
in UpdaterFIAS.Updater..ctor()
in UpdaterFIAS.Program.Main()
And I can see here my function getNameFile() is throwing an Exception. However, this isn't called in my code because I have empty OnStart(). So, how can I find what went wrong if the event log doesn't write anything ( if it is in the OnStart() ) ? And I can't attach a debugger to it because it throws this exception.
edit: Forgot to say, my code works correctly when I use windows forms but here I don't call anything in OnStart, the project builds without errors but I have an exception when starting the service.
EDIT 2:
Program.cs code:
namespace UpdaterFIAS
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new Updater()
};
ServiceBase.Run(ServicesToRun);
}
}
}
Updater.cs code:
namespace UpdaterFIAS
{
public partial class Updater : ServiceBase
{
public Updater()
{
InitializeComponent();
if (!System.Diagnostics.EventLog.SourceExists("MySource"))
{
System.Diagnostics.EventLog.CreateEventSource("MySource", "MyNewLog");
}
eventLog1.Source = "MySource";
eventLog1.Log = "MyNewLog";
}
FIASMainClass mainFIAS = new FIASMainClass();
protected override void OnStart(string[] args)
{
//timer1 = new System.Timers.Timer(5000);
//timer1.Elapsed += timer1_Elapsed;
//timer1.AutoReset = false;
//timer1.Enabled = true;
//ServiceStarterThread = new Thread(ServiceStarter);
//ServiceStarterThread.Start();
eventLog1.WriteEntry("In OnStart");
//mainFIAS.Start();
}
protected override void OnStop()
{
//if (updater != null && (updater.ThreadState != System.Threading.ThreadState.Aborted && updater.ThreadState != System.Threading.ThreadState.Stopped)) updater.Abort();
//if (log != null && (log.ThreadState != System.Threading.ThreadState.Aborted && log.ThreadState != System.Threading.ThreadState.Stopped)) log.Abort();
//log.Abort();
//timer1.Enabled = false;
//timer1.Dispose();
eventLog1.WriteEntry("In OnStop");
//mainFIAS.Stop();
}
}
}
EDIT 3:
FIASMainClass.cs code:
namespace UpdaterFIAS
{
class FIASMainClass
{
public FIASMainClass()
{ }
public void Start()
{
ServiceStarterThread = new Thread(ServiceStarter);
ServiceStarterThread.Start();
}
public void Stop()
{
if (updater != null && (updater.ThreadState != System.Threading.ThreadState.Aborted && updater.ThreadState != System.Threading.ThreadState.Stopped)) updater.Abort();
if (log != null && (log.ThreadState != System.Threading.ThreadState.Aborted && log.ThreadState != System.Threading.ThreadState.Stopped)) log.Abort();
}
private void ServiceStarter()
{
...
}
...
...
...
}
}
From you Stack Trace, the entry point is Program.Main. From there a new Updater is created and the getNameFiles is called. That would be the place to start.
As far as Debugging a windows service. You are right, this is indeed hard. There are two tricks I know. The first one is in the Main (or OnStart) to set a Thread.Sleep before you do anything. This way you have time to attach your debugger.
The other trick, is if your Visual Studio is on the same machine as your service, in the Main (or OnStart) add this line: Debugger.Launch(). This will tell the service to seek out Visual Studio for a debugging session. See here for more: Debugger.Launch() on windows service in Windows 8
I'm fairly new at working with Windows services but I found a peculiar incident and I would like some clarification. I have a Windows service written in C# which I install and start using the command line (great instructions found on stackoverflow). The main method of my service looks like this:
static void Main(string[] args)
{
if (args.Length == 0)
{
ServiceBase.Run(new MyServiceName());
}
else if (args.Length == 1)
{
const string name = "MyServiceName";
Type type = typeof(MyAssembly);
switch (args[0])
{
case "-install":
ServiceUtils.InstallService(name, type);
ServiceUtils.StartService(args, name);
break;
case "-uninstall":
ServiceUtils.StopService(name);
ServiceUtils.UninstallService(name, type);
break;
default:
throw new NotImplementedException();
}
}
}
When I debug, I ALWAYS send one parameter (-install) to the application. Because of this, the first if statement (if (args.Length == 0) is NEVER executed. This is expected and my service is installed and started just fine. However, if I remove that if statement and just leave the if (args.Length == 1) statement, my service installs correctly but it does not start and I get the following error:
Cannot start MyServiceName on computer '.'
My question is: Why is the code in the first if statement needed when it is NEVER executed in my application?
Here is the supporting code for the InstallService and StartService methods (which I got from stackoverflow also):
public static void InstallService(string serviceName, Type t)
{
if (IsInstalled(serviceName)) return;
try
{
Assembly a = t.Assembly;
using (AssemblyInstaller installer = GetInstaller(a))
{
IDictionary state = new Hashtable();
try
{
installer.Install(state);
installer.Commit(state);
}
catch
{
try
{
installer.Rollback(state);
}
catch
{ }
throw;
}
}
}
catch
{
throw;
}
}
public static void StartService(string[] args, string serviceName)
{
if (!IsInstalled(serviceName)) return;
Console.WriteLine("Service is installed. Attempting to start service.");
ServiceController sc = new ServiceController();
sc.ServiceName = serviceName;
if (sc.Status == ServiceControllerStatus.Stopped)
{
Console.WriteLine("Starting {0}: ", sc.ServiceName);
try
{
sc.Start(args);
sc.WaitForStatus(ServiceControllerStatus.Running);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
the first if statement (if (args.Length == 0) is NEVER executed
That's not correct, it is executed. By ServiceController.Start(). You cannot see this because the service controller starts your EXE again, creating another process. A service process this time, not a console process. One that you don't have a debugger attached to. If you remove that if statement then the service immediately exits after getting started. And the service controller correctly complains about that with the "Cannot start MyServiceName" exception message.
if (args.Length == 0)
{
ServiceBase.Run(new MyServiceName());
}
is run when the service is started by the Service Controller, as the Service Controller doesn't pass any arguments in to Main().
If you don't do ServiceBase.Run(new MyServiceName()), then your service will not respond to any commands from the Service Controller, and you get errors as the ones you see.
Main() is still the entry point of the application. The process is started as a separate step from starting the service(s) within.
It's actually possible to have multiple services running in the same process, and this way of handling things enables that. That is... not just the same exe program, but actually in the same running process.