I want to write a batch file or powershell script as a wrapper that can invoke apacheds.bat provided by ApacheDS to create a new ApacheDS instance and register it as a windows service.
Currently I use C# to create powershell process and pass argument: "-c & '$scriptFullName' -Service" to powershell process's CommandLine attribute. ($scriptFullName is the path to my powershell script). So when I want to start the service, the powershell.exe process will start and execute the command from CommandLine attribute.
However, now after running the powershell script, I find no new powershell.exe process with the passed argument in CommandLine attribute is created. The weird thing is that, in OnStart() method in #C code, p.Start() returns true, which means a new process is created. So now I am stuck on this part.
I attached part of the code below (C# code for service process, code for handling install, start, and service options). It would be greatly appreciated if anyone can help!
$adsStartCmd = "C:\apacheds\bin\apacheds.bat"
$serviceName = "eGranaryADS"
$installDir = "C:\apacheds\$serviceName" # Where to install the service files
$scriptCopy = "$installDir\$scriptName"
#-----------------------------------------------------------------------------#
# #
# Function $source #
# #
# Description C# source of the PSService.exe stub #
# #
# Arguments #
# #
# Notes The lines commented with "SET STATUS" and "EVENT LOG" are #
# optional. (Or blocks between "// SET STATUS [" and #
# "// SET STATUS ]" comments.) #
# SET STATUS lines are useful only for services with a long #
# startup time. #
# EVENT LOG lines are useful for debugging the service. #
# #
# History #
# #
#-----------------------------------------------------------------------------#
$scriptCopyCname = $scriptCopy -replace "\\", "\\" # Double backslashes. (The first \\ is a regexp with \ escaped; The second is a plain string.)
$source = @"
using System;
using System.ServiceProcess;
using System.Diagnostics;
using System.Runtime.InteropServices; // SET STATUS
using System.ComponentModel; // SET STATUS
public enum ServiceType : int { // SET STATUS [
SERVICE_WIN32_OWN_PROCESS = 0x00000010,
SERVICE_WIN32_SHARE_PROCESS = 0x00000020,
}; // SET STATUS ]
public enum ServiceState : int { // SET STATUS [
SERVICE_STOPPED = 0x00000001,
SERVICE_START_PENDING = 0x00000002,
SERVICE_STOP_PENDING = 0x00000003,
SERVICE_RUNNING = 0x00000004,
SERVICE_CONTINUE_PENDING = 0x00000005,
SERVICE_PAUSE_PENDING = 0x00000006,
SERVICE_PAUSED = 0x00000007,
}; // SET STATUS ]
[StructLayout(LayoutKind.Sequential)] // SET STATUS [
public struct ServiceStatus {
public ServiceType dwServiceType;
public ServiceState dwCurrentState;
public int dwControlsAccepted;
public int dwWin32ExitCode;
public int dwServiceSpecificExitCode;
public int dwCheckPoint;
public int dwWaitHint;
}; // SET STATUS ]
public enum Win32Error : int { // WIN32 errors that we may need to use
NO_ERROR = 0,
ERROR_APP_INIT_FAILURE = 575,
ERROR_FATAL_APP_EXIT = 713,
ERROR_SERVICE_NOT_ACTIVE = 1062,
ERROR_EXCEPTION_IN_SERVICE = 1064,
ERROR_SERVICE_SPECIFIC_ERROR = 1066,
ERROR_PROCESS_ABORTED = 1067,
};
public class Service_$serviceName : ServiceBase { // $serviceName may begin with a digit; The class name must begin with a letter
private System.Diagnostics.EventLog eventLog; // EVENT LOG
private ServiceStatus serviceStatus; // SET STATUS
public Service_$serviceName() {
ServiceName = "$serviceName";
CanStop = true;
CanPauseAndContinue = false;
AutoLog = true;
eventLog = new System.Diagnostics.EventLog(); // EVENT LOG [
if (!System.Diagnostics.EventLog.SourceExists(ServiceName)) {
System.Diagnostics.EventLog.CreateEventSource(ServiceName, "$logName");
}
eventLog.Source = ServiceName;
eventLog.Log = "$logName"; // EVENT LOG ]
EventLog.WriteEntry(ServiceName, "$exeName $serviceName()"); // EVENT LOG
}
[DllImport("advapi32.dll", SetLastError=true)] // SET STATUS
private static extern bool SetServiceStatus(IntPtr handle, ref ServiceStatus serviceStatus);
protected override void OnStart(string [] args) {
EventLog.WriteEntry(ServiceName, "$exeName OnStart() // Entry. Starting script '$scriptCopyCname' -Start"); // EVENT LOG
// Set the service state to Start Pending. // SET STATUS [
// Only useful if the startup time is long. Not really necessary here for a 2s startup time.
serviceStatus.dwServiceType = ServiceType.SERVICE_WIN32_OWN_PROCESS;
serviceStatus.dwCurrentState = ServiceState.SERVICE_START_PENDING;
serviceStatus.dwWin32ExitCode = 0;
serviceStatus.dwWaitHint = 2000; // It takes about 2 seconds to start PowerShell
SetServiceStatus(ServiceHandle, ref serviceStatus); // SET STATUS ]
// Start a child process with another copy of this script
try {
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "powershell.exe";
p.StartInfo.Arguments = "-ExecutionPolicy Bypass -c & '$scriptCopyCname' -Start"; // Works if path has spaces, but not if it contains ' quotes.
p.Start();
// Read the output stream first and then wait. (To avoid deadlocks says Microsoft!)
string output = p.StandardOutput.ReadToEnd();
// Wait for the completion of the script startup code, that launches the -Service instance
p.WaitForExit();
if (p.ExitCode != 0) throw new Win32Exception((int)(Win32Error.ERROR_APP_INIT_FAILURE));
// Success. Set the service state to Running. // SET STATUS
serviceStatus.dwCurrentState = ServiceState.SERVICE_RUNNING; // SET STATUS
} catch (Exception e) {
EventLog.WriteEntry(ServiceName, "$exeName OnStart() // Failed to start $scriptCopyCname. " + e.Message, EventLogEntryType.Error); // EVENT LOG
// Change the service state back to Stopped. // SET STATUS [
serviceStatus.dwCurrentState = ServiceState.SERVICE_STOPPED;
Win32Exception w32ex = e as Win32Exception; // Try getting the WIN32 error code
if (w32ex == null) { // Not a Win32 exception, but maybe the inner one is...
w32ex = e.InnerException as Win32Exception;
}
if (w32ex != null) { // Report the actual WIN32 error
serviceStatus.dwWin32ExitCode = w32ex.NativeErrorCode;
} else { // Make up a reasonable reason
serviceStatus.dwWin32ExitCode = (int)(Win32Error.ERROR_APP_INIT_FAILURE);
} // SET STATUS ]
} finally {
serviceStatus.dwWaitHint = 0; // SET STATUS
SetServiceStatus(ServiceHandle, ref serviceStatus); // SET STATUS
EventLog.WriteEntry(ServiceName, "$exeName OnStart() // Exit"); // EVENT LOG
}
}
protected override void OnStop() {
EventLog.WriteEntry(ServiceName, "$exeName OnStop() // Entry"); // EVENT LOG
// Start a child process with another copy of ourselves
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "powershell.exe";
p.StartInfo.Arguments = "-c & '$scriptCopyCname' -Stop"; // Works if path has spaces, but not if it contains ' quotes.
p.Start();
// Read the output stream first and then wait.
string output = p.StandardOutput.ReadToEnd();
// Wait for the PowerShell script to be fully stopped.
p.WaitForExit();
// Change the service state back to Stopped. // SET STATUS
serviceStatus.dwCurrentState = ServiceState.SERVICE_STOPPED; // SET STATUS
SetServiceStatus(ServiceHandle, ref serviceStatus); // SET STATUS
EventLog.WriteEntry(ServiceName, "$exeName OnStop() // Exit"); // EVENT LOG
}
public static void Main() {
System.ServiceProcess.ServiceBase.Run(new Service_$serviceName());
}
}
"@
#-----------------------------------------------------------------------------#
# #
# Function Main #
# #
# Description Execute the specified actions #
# #
# Arguments See the Param() block at the top of this script #
# #
# Notes #
# #
# History #
# #
#-----------------------------------------------------------------------------#
# Check if we're running as a real user, or as the SYSTEM = As a service
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$userName = $identity.Name # Ex: "NT AUTHORITY\SYSTEM" or "Domain\Administrator"
$authority,$name = $username -split "\\"
$isSystem = $identity.IsSystem # Do not test ($userName -eq "NT AUTHORITY\SYSTEM"), as this fails in non-English systems.
if ($Install) { # Install the service
# Check if it's necessary
try {
$pss = Get-Service $serviceName -ea stop # Will error-out if not installed
# Check if this script is newer than the installed copy.
if ((Get-Item $scriptCopy -ea SilentlyContinue).LastWriteTime -lt (Get-Item $scriptFullName -ea SilentlyContinue).LastWriteTime) {
Write-Verbose "Service $serviceName is already Installed, but requires upgrade"
& $scriptFullName -Remove
throw "continue"
} else {
Write-Verbose "Service $serviceName is already Installed, and up-to-date"
}
exit 0
} catch {
# This is the normal case here. Do not throw or write any error!
Write-Debug "Installation is necessary" # Also avoids a ScriptAnalyzer warning
# And continue with the installation.
}
if (!(Test-Path $installDir)) {
New-Item -ItemType directory -Path $installDir | Out-Null
}
# Copy the service script into the installation directory
if ($ScriptFullName -ne $scriptCopy) {
Write-Verbose "Installing $scriptCopy"
Copy-Item $ScriptFullName $scriptCopy
}
# Generate the service .EXE from the C# source embedded in this script
try {
Write-Verbose "Compiling $exeFullName"
Add-Type -TypeDefinition $source -Language CSharp -OutputAssembly $exeFullName -OutputType ConsoleApplication -ReferencedAssemblies "System.ServiceProcess" -Debug:$false
} catch {
$msg = $_.Exception.Message
Write-error "Failed to create the $exeFullName service stub. $msg"
exit 1
}
# Register the service
Write-Verbose "Registering service $serviceName"
$pss = New-Service $serviceName $exeFullName -DisplayName $serviceDisplayName -Description $ServiceDescription -StartupType Automatic
return
}
if ($Start) { # Start the service
if ($isSystem) { # If running as SYSTEM, ie. invoked as a service
# Do whatever is necessary to start the service script instance
Log "$scriptName -Start: Starting script '$scriptFullName' -Service"
Write-EventLog -LogName $logName -Source $serviceName -EventId 1001 -EntryType Information -Message "$scriptName -Start: Starting script '$scriptFullName' -Service"
Start-Process Powershell.exe -ArgumentList ("-c & '$scriptFullName' -Service")
} else {
Write-Verbose "Starting service $serviceName"
Write-EventLog -LogName $logName -Source $serviceName -EventId 1002 -EntryType Information -Message "$scriptName -Start: Starting service $serviceName"
Start-Service $serviceName # Ask Service Control Manager to start it
}
return
}
if ($Service) { # Run the service
Write-EventLog -LogName $logName -Source $serviceName -EventId 1005 -EntryType Information -Message "$scriptName -Service # Beginning background job"
# Do the service background job
try
{
#region ApacheDS start
Log "$scriptName Starting $adsStartCmd with parameteres start -f -p $adsPort -m $adsMemory"
# &$adsStartCmd start -f -p $adsPort -m $adsMemory
&$adsStartCmd test3 start
#endregion
}
catch
{ # An exception occurred while runnning the service
$msg = $_.Exception.Message
$line = $_.InvocationInfo.ScriptLineNumber
Log "$scriptName -Service # Error at line ${line}: $msg"
}
finally
{
# Invoked in all cases: Exception or normally by -Stop
# Flush all leftover events (There may be some that arrived after we exited the while event loop, but before we unregistered the events)
$events = Get-Event | Remove-Event
# Log a termination event, no matter what the cause is.
Write-EventLog -LogName $logName -Source $serviceName -EventId 1006 -EntryType Information -Message "$script -Service # Exiting"
Log "$scriptName -Service # Exiting"
}
return
}
...
User contributions licensed under CC BY-SA 3.0