vs Addin failing trying to Execute command - c#

I have VS2012 professional and would very much like my finely honed tests to run at the end of a build.
I thought I could do by writing an addin quite simply. So I have this in a c# addin.
public void OnConnection(object app, ext_ConnectMode cM, object aI, ref Array cust) {
_applicationObject = (DTE2)app;
_addInInstance = (AddIn)aI;
if (_applicationObject != null) {
_bldevents = _applicationObject.Events.BuildEvents;
_bldevents.OnBuildDone += _bldevents_OnBuildDone;
}
}
void _bldevents_OnBuildDone(vsBuildScope Scope, vsBuildAction Action) {
try {
_applicationObject.ExecuteCommand("TestExplorer.RunAllTests");
} catch(Exception e) {
string d = " + " + e.HResult;
Clipboard.SetText(e.Message + " ~ " + e.HResult);
MessageBox.Show(e.Message);
}
}
However when I do a build I get the error Error HRESULT E_FAIL has been returned from a call to a COM component. ~ -2147467259
ExecuteCommand work on stuff like File.NewFile run TestExplorer.RunAllTests in the command window with no problems.
Is there some setup that needs doing or have ms somehow stymied the behaviour because they want me to bankrupt myself and get the ultimate version ;)
Any thoughts?

It looks like TestExplorer.RunAllTests performs build first, your _bldevents_OnBuildDone is called again, TestExplorer.RunAllTests is called again and second time it throws the exception.
You can add the IsCommandAvailable check to prevent this recursion:
if (IsCommandAvailable("TestExplorer.RunAllTests"))
_applicationObject.ExecuteCommand("TestExplorer.RunAllTests");
bool IsCommandAvailable(string command)
{
Commands2 commands = (Commands2)(_applicationObject.Commands);
if (commands == null)
return false;
Command dte_command = commands.Item(command, 0);
if (dte_command == null)
return false;
return dte_command.IsAvailable;
}

Related

ScriptBuffer throws NullReferenceException when called from an Event as part of a loop - SSIS Script Component [duplicate]

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 1 year ago.
I have an SSIS package which calls a Data Flow Task as part of a loop which iterates different end-point addresses (out of scope).
The Data Flow Task has a source Script Component responsible for calling a REST API and creating a row for each result.
There are 3 output buffers;
1. actual data row
2. error row
3. monitoring
The monitoring buffer used for telemetry and is populated through an event (EventHander) that is fired every time the API makes a request.
During the first iteration of the ForEach int the Control Flow loop, everything runs as expected, all the buffers produce the correct rows.
However, during the next iterations, the monitoring buffer which is populated within the event throws;
System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.SqlServer.Dts.Pipeline.ScriptComponentHost.HandleUserException(Exception e)
at Microsoft.SqlServer.Dts.Pipeline.ScriptComponentHost.PrimeOutput(Int32 outputs, Int32[] outputIDs, PipelineBuffer[] buffers)
at Microsoft.SqlServer.Dts.Pipeline.ManagedComponentHost.HostPrimeOutput(IDTSManagedComponentWrapper100 wrapper, Int32 outputs, Int32[] outputIDs, IDTSBuffer100[] buffers, IntPtr ppBufferWirePacket)
I don't understand why the MonitoringBuffer is not initialised in the proceeding iterations.
The exception occurs while calling MonitoringBuffer.AddRow();.
Here's the whole Script Component simplified for readability:
[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
private string ClientCode { get { return Variables.ErplyClientCode; } }
private string Username { get { return Variables.ErplyUsername; } }
private string Password { get { return Variables.ErplyPassword; } }
private bool IsTest { get { return Variables.IsTest; } }
private int ErplyRecordsPerPage { get { return Variables.ErplyRecordsPerPage; } }
private string ErplyDebugOutputPath { get { return Variables.ErplyDebugOutputPath; } }
private DateTime ChangeSince { get { return Variables.ChangeSince; } }
private int records { get; set; }
private int errors { get; set; }
private string rawFolder { get; set; }
public override void PreExecute()
{
base.PreExecute();
}
public override void PostExecute()
{
base.PostExecute();
}
public override void CreateNewOutputRows()
{
ErplyAPI.OnPreRequestEvent += new EventHandler<EAPIEvent>(ErplyAPI_OnPreRequestEvent);
var staff = ErplyAPI.getStaff(ClientCode, Username, Password, ChangeSince, ErplyRecordsPerPage, IsTest);
foreach (var p in staff.List)
{
try
{
if (!p.IsError)
{
EmployeeBuffer.AddRow();
EmployeeBuffer.employeeID = p.employeeID;
}
else
{
ErrorBuffer.AddRow();
ErrorBuffer.employeeID = p.employeeID;
ErrorBuffer.Error = p.Error.Message.Trim() + "\n" + p.Error.StackTrace;
errors++;
}
records++;
}
catch (Exception ex)
{
this.ComponentMetaData.FireWarning(0, "Script", ex.Message + "\n" + ex.StackTrace, string.Empty, 0);
}
}
EmployeeBuffer.SetEndOfRowset();
ErrorBuffer.SetEndOfRowset();
}
private void ErplyAPI_OnPreRequestEvent(object sender, EAPIEvent e)
{
var request = string.Empty;
var sessionKey = string.Empty;
bool fireAgain = true;
if (e == null)
{
ComponentMetaData.FireWarning(0, "SC_ERPLY_API", string.Format("EAPIEvent is NULL in ErplyAPI_OnPreRequestEvent. Amonit did not log the Erply request."), string.Empty, 0);
return;
}
if (e.eAPI == null)
{
ComponentMetaData.FireWarning(0, "SC_ERPLY_API", string.Format("EAPIEvent.eAPI is NULL in ErplyAPI_OnPreRequestEvent. Amonit did not log the Erply request."), string.Empty, 0);
return;
}
try
{
if (e.Parameters != null && e.Parameters.ContainsKey("request"))
request = e.Parameters["request"].ToString();
if (request != "verifyUser" && e.Parameters != null && e.Parameters.ContainsKey("sessionKey"))
sessionKey = e.Parameters["sessionKey"].ToString();
}
catch (Exception ex)
{
ComponentMetaData.FireWarning(0, "SC_ERPLY_API", string.Format("Error occurred assigning variables from EAPIEvent parameters in ErplyAPI_OnPreRequestEvent. {0} {1}", ex.Message, ex.StackTrace), string.Empty, 0);
}
try
{
MonitoringBuffer.AddRow(); // Exception occurs here
MonitoringBuffer.Request = ResizeString(request, 255);
MonitoringBuffer.SessionKey = ResizeString(sessionKey, 128);
}
catch (Exception ex)
{
var message = string.Format("Error occurred outputting Erply request in ErplyAPI_OnPreRequestEvent. {0} {1}", ex.Message, ex.StackTrace);
MonitoringBuffer.ErrorMessage = ResizeString(message, 8000);
ComponentMetaData.FireWarning(0, "SC_ERPLY_API", message, string.Empty, 0);
}
finally
{
MonitoringBuffer.EndOfRowset();
}
}
}
I sorted the problem out.
The exception was being raised when the variable dispenser was being accessed from the Event. For some reason the GetValueWithContext(ScriptComponent.EvaluatorContext) is being dropped during the second call. Why this happens is beyond me.
The solution is simple, assign the variables from the variables dispenser to a local property or variable in the OnPreExecute function.
It's also good practice to not call the variable dispenser in the CreateNewOutputRows as it cause variable locking.
I ran into this issue too, but my solution was a little different -- moving the variable assignments into PreExecute() didn't help.
Instead, what I'd done is that I wanted to parse three different files, and read each of them with a Script Component. Their columns were kinda similar, so I created one Data Flow task, made sure it worked, then copied it and modified each copy to reflect the differences in the files. Running each individual Data Flow task was successful, but when I tried to run two of them, one after the other in a loop, I got a NullReferenceException from HostPrimeOutput() after calling the OutputBuffer.AddRow() method in my Script Component.
It turns out that when I copied each Data Flow task, the Script Components all kept the same namespace, and I guess it doesn't like that. So, I created brand new Script Components, set up all the output columns again (ugh!), copied the body of the script over, and it's happy.

Azure Service Bus 3.2.2 BeginReceive()

I need to upgrade our service bus nuget package to 3.2.2 (think the evenprocessor host requires it) but I have always kept our service bus project lib at 2.8.2. This is mainly due to the fact that BeginReceive() and EndReceive() looks to have been removed. Is there any reason or anyway I can easily convert this
public void StartReceiving(RecieverCallback callback, TimeSpan waittime, object state = null)
{
this._recieverCallback = callback;
_queueClient = this.MessagingFactory.CreateQueueClient(QueueName, ReceiveMode);
// make initial async call
_asyncresult = _queueClient.BeginReceive(waittime, ReceiveDone, _queueClient);
}
public void ReceiveDone(IAsyncResult result)
{
if (result != null)
{
try
{
var tmpClient = result.AsyncState as QueueClient;
var brokeredMessage = tmpClient.EndReceive(result);
if (brokeredMessage != null)
{
if (ReceiveMode == ReceiveMode.PeekLock)
{
brokeredMessage.Complete();
}
var tmpMessage = brokeredMessage.GetBody<T>();
ProcessMessageProperties(tmpMessage, brokeredMessage);
_recieverCallback(tmpMessage);
}
}
catch (Exception ex)
{
_logger.Fatal("ReceiveDone: {0}", ex.Message);
Console.WriteLine(ex.Message);
}
}
// do recieve for next message
_asyncresult = _queueClient.BeginReceive(ReceiveDone, _queueClient);
}
Image showing the error
Following image shows what happens if I upgrade servicebus to 3.2.2 which I believe will solve the original error (program running 3.2.2, lib project running 2.8.x)
I figured it out see link
https://gist.github.com/sitereactor/8953583
If anyone has a similar issue, let me know and will post my code but its 95% the same as per the link.

Return the calling function

I use C# for testing purpose. A test consists of several test steps. If one test step fails, the whole test should be aborted. One test step could look like this:
Variable1.Value = 1;
Variable1.write();
Variable1.read();
if (Variable1.Value != 1)
{
Console.WriteLine("Fail");
return; //this return aborts the test
}
//next test steps
I'd like to transfer some commands into own functions to allow efficient test case programming. The function for the code above would look like this.
private void verifyValue (TypeOfVariable Var, double Value)
{
Var.read();
if (Var.Value != Value)
{
Console.WriteLine("Fail");
return;
}
}
And the test would look like this
Variable1.Value = 1;
Variable1.write();
verifyValue(Variable1, 1);
//next test steps
My Problem is now, that the return in function verifyValue only effects verifyValue but not the calling function (aka test).
Is there any possibility to abort the calling function?
This is typically done via Exceptions. They propagate through the call stack automatically. Here's an example based on your code:
public class TestFailedException : Exception
{
public TestFailedException(string message) : base(message) { }
}
void Test()
{
try
{
Variable1.Value = 1;
Variable1.write();
verifyValue(Variable1, 1);
//next test steps
...
Console.WriteLine("Test succeeded");
}
catch (TestFailedException ex)
{
Console.WriteLine("Test failed: " + ex.Message);
}
}
private void verifyValue(TypeOfVariable Var, double Value)
{
Var.read();
if (Var.Value != Value)
{
throw new TestFailedException("Actual value: " + Var.Value.ToString()
+ ", expected value: " + Value.ToString());
}
}
Its better if you use Transaction and then on any exception abort all the operation. For your current code you can through an exception and let the program stop itself. like:
throw new Exception("Test Failed - Stopping the program execution");

Getting AccessViolationException on client PCs

Does anyone have experience using EZTwain's BARCODE_Recognize function?
I'm wondering why I'm getting an AccessViolationException in my program when trying to use the Dosadi EZTwain library to recognize bar codes from scanned images.
This program runs flawlessly on our dev environment, so I'm having a hard time tracking down what exactly is the cause of the problem when it runs on the client's PCs.
I've found through using the ildasm.exe program that the exception originates in this method of my barcode recognition utility class.
Edit: I'm wondering if it could have anything to do with our users not being set up as administrators? Since we have no problems at all running it here as admin, then they get this exception the first time it calls this GetBarcode method?
Edit: Is there anything else anyone else would need to see from my code to help me track down this problem?
The ildasm where the error occurs looks like this. The error is on IL_00bb: ldloc.3 which is the first parameter "image".
//000053: count = EZTwain.BARCODE_Recognize(image, -1, -1);
IL_00bb: ldloc.3
IL_00bc: ldc.i4.m1
IL_00bd: ldc.i4.m1
IL_00be: call int32 Dosadi.EZTwain.EZTwain/*02000005*/::BARCODE_Recognize(native int,
int32,
int32) /* 060001E4 */
IL_00c3: stloc.1
GetBarcode
public static string GetBarcode(Bitmap bImage, out BarcodeType barcodeType)
{
barcodeType = BarcodeType.NotBarcode;
string selectedBarcode = null;
int count = 0;
IntPtr hImage = IntPtr.Zero, image = IntPtr.Zero;
List<string> barcodes = new List<string>();
try
{
try
{
if (bImage.Width == 0 || bImage.Height == 0)
{
return null;
}
hImage = bImage.GetHbitmap();
if (hImage == IntPtr.Zero)
{
return null;
}
image = EZTwain.DIB_FromBitmap(hImage, IntPtr.Zero);
}
catch (Exception ex)
{
logger.LogException(LogLevel.Debug,
"Exception in GetBarcode(): inner try-catch block", ex);
throw;
}
finally
{
if (hImage != IntPtr.Zero)
DeleteObject(hImage);
}
EZTwain.BARCODE_SetDirectionFlags(-1);
count = EZTwain.BARCODE_Recognize(image, -1, -1);
UtilDebug("Found {0} barcodes in image on first attempt.", count);
for (int i = 0; i < count; i++)
{
barcodes.Add(EZTwain.BARCODE_Text(i));
}
foreach (string code in barcodes)
{
UtilDebug("Processing barcode \"{0}\".", code);
if (ProcessBarcodeType(code) == BarcodeType.CoversheetBarcode || ProcessBarcodeType(code) == BarcodeType.RegularBarcode)
{
barcodeType = ProcessBarcodeType(code);
selectedBarcode = code;
UtilDebug("SelectedBarcode set to \"{0}\".", code);
break;
}
}
}
catch (Exception ex)
{
logger.LogException(LogLevel.Debug, "Exception in GetBarcode(): outer try-catch block", ex);
throw;
}
finally
{
if (image != IntPtr.Zero)
EZTwain.DIB_Free(image);
barcodes.Clear();
}
//Find one that is an ASI barcode before return
return selectedBarcode;
}
The way you're interacting with the barcode engine looks OK to me. I would guess maybe the barcode engine is choking on something internally, maybe there's something about that image.
BTW I think newer editions of the EZTwain toolkit include a function that does that conversion from System.Drawing.Bitmap to EZTwain's 'HDIB'... yes: DIB_FromImage. It works differently from your code, might be worth trying.
I would start by collecting a log (using DosadiLog, an app in your Start menu under EZTwain) on the failing machine, collected while reproducing the crash. Either post it here or - probably more legit, open an issue on the Atalasoft Support Portal and attach it to that.
Sorry it took us so long to notice your post here, we'll set up better monitoring now.

Am I Running as a Service

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.

Categories

Resources