You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by jg...@apache.org on 2019/05/28 09:46:55 UTC

svn commit: r1860225 [9/16] - in /tomee/deps/branches/commons-daemon: ./ src/ src/assembly/ src/changes/ src/docs/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/commons/ src/main/java/org/apache/commons/...

Added: tomee/deps/branches/commons-daemon/src/native/windows/apps/prunsrv/prunsrv.c
URL: http://svn.apache.org/viewvc/tomee/deps/branches/commons-daemon/src/native/windows/apps/prunsrv/prunsrv.c?rev=1860225&view=auto
==============================================================================
--- tomee/deps/branches/commons-daemon/src/native/windows/apps/prunsrv/prunsrv.c (added)
+++ tomee/deps/branches/commons-daemon/src/native/windows/apps/prunsrv/prunsrv.c Tue May 28 09:46:53 2019
@@ -0,0 +1,1841 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* ====================================================================
+ * prunsrv -- Service Runner.
+ * Contributed by Mladen Turk <mt...@apache.org>
+ * 05 Aug 2003
+ * ====================================================================
+ */
+
+/* Force the JNI vprintf functions */
+#define _DEBUG_JNI  1
+#include "apxwin.h"
+#include "private.h"
+#include "prunsrv.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <fcntl.h>
+#include <io.h>         /* _open_osfhandle */
+
+#ifndef  MIN
+#define  MIN(a,b)    (((a)<(b)) ? (a) : (b))
+#endif
+
+#define STDIN_FILENO  0
+#define STDOUT_FILENO 1
+#define STDERR_FILENO 2
+#define ONE_MINUTE    (60 * 1000)
+
+#ifdef _WIN64
+#define KREG_WOW6432  KEY_WOW64_32KEY
+#define PRG_BITS      64
+#else
+#define KREG_WOW6432  0
+#define PRG_BITS      32
+#endif
+
+typedef struct APX_STDWRAP {
+    LPCWSTR szLogPath;
+    LPCWSTR szStdOutFilename;
+    LPCWSTR szStdErrFilename;
+    FILE   *fpStdOutFile;
+    FILE   *fpStdErrFile;
+} APX_STDWRAP;
+
+/* Use static variables instead of #defines */
+static LPCWSTR  PRSRV_AUTO        = L"auto";
+static LPCWSTR  PRSRV_JAVA        = L"java";
+static LPCWSTR  PRSRV_JVM         = L"jvm";
+static LPCWSTR  PRSRV_JDK         = L"jdk";
+static LPCWSTR  PRSRV_JRE         = L"jre";
+static LPCWSTR  PRSRV_MANUAL      = L"manual";
+static LPCWSTR  PRSRV_JBIN        = L"\\bin\\java.exe";
+static LPCWSTR  PRSRV_PBIN        = L"\\bin";
+static LPCWSTR  PRSRV_SIGNAL      = L"SIGNAL";
+static LPCWSTR  PRSV_JVMOPTS9     = L"JDK_JAVA_OPTIONS=";
+static LPCWSTR  STYPE_INTERACTIVE = L"interactive";
+
+static LPWSTR       _service_name = NULL;
+/* Allowed procrun commands */
+static LPCWSTR _commands[] = {
+    L"TS",      /* 1 Run Service as console application (default)*/
+    L"RS",      /* 2 Run Service */
+    L"ES",      /* 3 Execute start */
+    L"SS",      /* 4 Stop Service */
+    L"US",      /* 5 Update Service parameters */
+    L"IS",      /* 6 Install Service */
+    L"DS",      /* 7 Delete Service */
+    L"?",       /* 8 Help */
+    L"VS",      /* 9 Version */
+    NULL
+};
+
+static LPCWSTR _altcmds[] = {
+    L"run",         /* 1 Run Service as console application (default)*/
+    L"service",     /* 2 Run Service */
+    L"start",       /* 3 Start Service */
+    L"stop",        /* 4 Stop Service */
+    L"update",      /* 5 Update Service parameters */
+    L"install",     /* 6 Install Service */
+    L"delete",      /* 7 Delete Service */
+    L"help",        /* 8 Help */
+    L"version",     /* 9 Version */
+    NULL
+};
+
+/* Allowed procrun parameters */
+static APXCMDLINEOPT _options[] = {
+
+/* 0  */    { L"Description",       L"Description",     NULL,           APXCMDOPT_STR | APXCMDOPT_SRV, NULL, 0},
+/* 1  */    { L"DisplayName",       L"DisplayName",     NULL,           APXCMDOPT_STR | APXCMDOPT_SRV, NULL, 0},
+/* 2  */    { L"Install",           L"ImagePath",       NULL,           APXCMDOPT_STE | APXCMDOPT_SRV, NULL, 0},
+/* 3  */    { L"ServiceUser",       L"ServiceUser",     NULL,           APXCMDOPT_STR | APXCMDOPT_SRV, NULL, 0},
+/* 4  */    { L"ServicePassword",   L"ServicePassword", NULL,           APXCMDOPT_STR | APXCMDOPT_SRV, NULL, 0},
+/* 5  */    { L"Startup",           L"Startup",         NULL,           APXCMDOPT_STR | APXCMDOPT_SRV, NULL, 0},
+/* 6  */    { L"Type",              L"Type",            NULL,           APXCMDOPT_STR | APXCMDOPT_SRV, NULL, 0},
+
+/* 7  */    { L"DependsOn",         L"DependsOn",       NULL,           APXCMDOPT_MSZ | APXCMDOPT_REG, NULL, 0},
+/* 8  */    { L"Environment",       L"Environment",     NULL,           APXCMDOPT_MSZ | APXCMDOPT_REG, NULL, 0},
+/* 9  */    { L"User",              L"User",            NULL,           APXCMDOPT_STR | APXCMDOPT_REG, NULL, 0},
+/* 10 */    { L"Password",          L"Password",        NULL,           APXCMDOPT_BIN | APXCMDOPT_REG, NULL, 0},
+/* 11 */    { L"LibraryPath",       L"LibraryPath",     NULL,           APXCMDOPT_STE | APXCMDOPT_REG, NULL, 0},
+
+/* 12 */    { L"JavaHome",          L"JavaHome",        L"Java",        APXCMDOPT_STE | APXCMDOPT_REG, NULL, 0},
+/* 13 */    { L"Jvm",               L"Jvm",             L"Java",        APXCMDOPT_STE | APXCMDOPT_REG, NULL, 0},
+/* 14 */    { L"JvmOptions",        L"Options",         L"Java",        APXCMDOPT_MSZ | APXCMDOPT_REG, NULL, 0},
+/* 15 */    { L"JvmOptions9",       L"Options9",        L"Java",        APXCMDOPT_MSZ | APXCMDOPT_REG, NULL, 0},
+/* 16 */    { L"Classpath",         L"Classpath",       L"Java",        APXCMDOPT_STE | APXCMDOPT_REG, NULL, 0},
+/* 17 */    { L"JvmMs",             L"JvmMs",           L"Java",        APXCMDOPT_INT | APXCMDOPT_REG, NULL, 0},
+/* 18 */    { L"JvmMx",             L"JvmMx",           L"Java",        APXCMDOPT_INT | APXCMDOPT_REG, NULL, 0},
+/* 19 */    { L"JvmSs",             L"JvmSs",           L"Java",        APXCMDOPT_INT | APXCMDOPT_REG, NULL, 0},
+
+/* 20 */    { L"StopImage",         L"Image",           L"Stop",        APXCMDOPT_STE | APXCMDOPT_REG, NULL, 0},
+/* 21 */    { L"StopPath",          L"WorkingPath",     L"Stop",        APXCMDOPT_STE | APXCMDOPT_REG, NULL, 0},
+/* 22 */    { L"StopClass",         L"Class",           L"Stop",        APXCMDOPT_STR | APXCMDOPT_REG, NULL, 0},
+/* 23 */    { L"StopParams",        L"Params",          L"Stop",        APXCMDOPT_MSZ | APXCMDOPT_REG, NULL, 0},
+/* 24 */    { L"StopMethod",        L"Method",          L"Stop",        APXCMDOPT_STR | APXCMDOPT_REG, NULL, 0},
+/* 25 */    { L"StopMode",          L"Mode",            L"Stop",        APXCMDOPT_STR | APXCMDOPT_REG, NULL, 0},
+/* 26 */    { L"StopTimeout",       L"Timeout",         L"Stop",        APXCMDOPT_INT | APXCMDOPT_REG, NULL, 0},
+
+/* 27 */    { L"StartImage",        L"Image",           L"Start",       APXCMDOPT_STE | APXCMDOPT_REG, NULL, 0},
+/* 28 */    { L"StartPath",         L"WorkingPath",     L"Start",       APXCMDOPT_STE | APXCMDOPT_REG, NULL, 0},
+/* 29 */    { L"StartClass",        L"Class",           L"Start",       APXCMDOPT_STR | APXCMDOPT_REG, NULL, 0},
+/* 30 */    { L"StartParams",       L"Params",          L"Start",       APXCMDOPT_MSZ | APXCMDOPT_REG, NULL, 0},
+/* 31 */    { L"StartMethod",       L"Method",          L"Start",       APXCMDOPT_STR | APXCMDOPT_REG, NULL, 0},
+/* 32 */    { L"StartMode",         L"Mode",            L"Start",       APXCMDOPT_STR | APXCMDOPT_REG, NULL, 0},
+
+/* 33 */    { L"LogPath",           L"Path",            L"Log",         APXCMDOPT_STE | APXCMDOPT_REG, NULL, 0},
+/* 34 */    { L"LogPrefix",         L"Prefix",          L"Log",         APXCMDOPT_STR | APXCMDOPT_REG, NULL, 0},
+/* 35 */    { L"LogLevel",          L"Level",           L"Log",         APXCMDOPT_STR | APXCMDOPT_REG, NULL, 0},
+/* 36 */    { L"StdError",          L"StdError",        L"Log",         APXCMDOPT_STE | APXCMDOPT_REG, NULL, 0},
+/* 37 */    { L"StdOutput",         L"StdOutput",       L"Log",         APXCMDOPT_STE | APXCMDOPT_REG, NULL, 0},
+/* 38 */    { L"LogJniMessages",    L"LogJniMessages",  L"Log",         APXCMDOPT_INT | APXCMDOPT_REG, NULL, 1},
+/* 39 */    { L"PidFile",           L"PidFile",         L"Log",         APXCMDOPT_STR | APXCMDOPT_REG, NULL, 0},
+/* 40 */    { L"Rotate",            L"Rotate",          L"Log",         APXCMDOPT_INT | APXCMDOPT_REG, NULL, 0},
+            /* NULL terminate the array */
+            { NULL }
+};
+
+#define GET_OPT_V(x)  _options[x].szValue
+#define GET_OPT_I(x)  _options[x].dwValue
+#define GET_OPT_T(x)  _options[x].dwType
+
+#define ST_DESCRIPTION      GET_OPT_T(0)
+#define ST_DISPLAYNAME      GET_OPT_T(1)
+#define ST_INSTALL          GET_OPT_T(2)
+#define ST_SUSER            GET_OPT_T(3)
+#define ST_SPASSWORD        GET_OPT_T(4)
+#define ST_STARTUP          GET_OPT_T(5)
+#define ST_TYPE             GET_OPT_T(6)
+
+#define SO_DESCRIPTION      GET_OPT_V(0)
+#define SO_DISPLAYNAME      GET_OPT_V(1)
+#define SO_INSTALL          GET_OPT_V(2)
+#define SO_SUSER            GET_OPT_V(3)
+#define SO_SPASSWORD        GET_OPT_V(4)
+#define SO_STARTUP          GET_OPT_V(5)
+#define SO_TYPE             GET_OPT_V(6)
+
+#define SO_DEPENDSON        GET_OPT_V(7)
+#define SO_ENVIRONMENT      GET_OPT_V(8)
+
+#define SO_USER             GET_OPT_V(9)
+#define SO_PASSWORD         GET_OPT_V(10)
+#define SO_LIBPATH          GET_OPT_V(11)
+
+#define SO_JAVAHOME         GET_OPT_V(12)
+#define SO_JVM              GET_OPT_V(13)
+#define SO_JVMOPTIONS       GET_OPT_V(14)
+#define SO_JVMOPTIONS9      GET_OPT_V(15)
+#define SO_CLASSPATH        GET_OPT_V(16)
+#define SO_JVMMS            GET_OPT_I(17)
+#define SO_JVMMX            GET_OPT_I(18)
+#define SO_JVMSS            GET_OPT_I(19)
+
+#define SO_STOPIMAGE        GET_OPT_V(20)
+#define SO_STOPPATH         GET_OPT_V(21)
+#define SO_STOPCLASS        GET_OPT_V(22)
+#define SO_STOPPARAMS       GET_OPT_V(23)
+#define SO_STOPMETHOD       GET_OPT_V(24)
+#define SO_STOPMODE         GET_OPT_V(25)
+#define SO_STOPTIMEOUT      GET_OPT_I(26)
+
+#define SO_STARTIMAGE       GET_OPT_V(27)
+#define SO_STARTPATH        GET_OPT_V(28)
+#define SO_STARTCLASS       GET_OPT_V(29)
+#define SO_STARTPARAMS      GET_OPT_V(30)
+#define SO_STARTMETHOD      GET_OPT_V(31)
+#define SO_STARTMODE        GET_OPT_V(32)
+
+#define SO_LOGPATH          GET_OPT_V(33)
+#define SO_LOGPREFIX        GET_OPT_V(34)
+#define SO_LOGLEVEL         GET_OPT_V(35)
+
+#define SO_STDERROR         GET_OPT_V(36)
+#define SO_STDOUTPUT        GET_OPT_V(37)
+#define SO_JNIVFPRINTF      GET_OPT_I(38)
+#define SO_PIDFILE          GET_OPT_V(39)
+#define SO_LOGROTATE        GET_OPT_I(40)
+
+static SERVICE_STATUS        _service_status;
+static SERVICE_STATUS_HANDLE _service_status_handle = NULL;
+/* Set if launched by SCM   */
+static BOOL                  _service_mode = FALSE;
+/* JVM used as worker       */
+static BOOL                  _jni_startup  = FALSE;
+/* JVM used for shutdown    */
+static BOOL                  _jni_shutdown = FALSE;
+/* Java used as worker       */
+static BOOL                  _java_startup  = FALSE;
+/* Java used for shutdown    */
+static BOOL                  _java_shutdown = FALSE;
+/* Global variables and objects */
+static APXHANDLE    gPool;
+static APXHANDLE    gWorker;
+static APX_STDWRAP  gStdwrap;           /* stdio/stderr redirection */
+static int          gExitval;
+static LPWSTR       gStartPath;
+
+static LPWSTR   _jni_jvmpath              = NULL;   /* Path to jvm dll */
+static LPSTR    _jni_jvmoptions           = NULL;   /* jvm options */
+static LPSTR    _jni_jvmoptions9          = NULL;   /* java 9+ options */
+
+static LPSTR    _jni_classpath            = NULL;
+static LPCWSTR  _jni_rparam               = NULL;    /* Startup  arguments */
+static LPCWSTR  _jni_sparam               = NULL;    /* Shutdown arguments */
+static LPSTR    _jni_rmethod              = NULL;    /* Startup  method */
+static LPSTR    _jni_smethod              = NULL;    /* Shutdown method */
+static LPSTR    _jni_rclass               = NULL;    /* Startup  class */
+static LPSTR    _jni_sclass               = NULL;    /* Shutdown class */
+static HANDLE gShutdownEvent = NULL;
+static HANDLE gSignalEvent   = NULL;
+static HANDLE gSignalThread  = NULL;
+static HANDLE gPidfileHandle = NULL;
+static LPWSTR gPidfileName   = NULL;
+static BOOL   gSignalValid   = TRUE;
+static APXJAVA_THREADARGS gRargs;
+static APXJAVA_THREADARGS gSargs;
+
+DWORD WINAPI eventThread(LPVOID lpParam)
+{
+    DWORD dwRotateCnt = SO_LOGROTATE;
+
+    for (;;) {
+        DWORD dw = WaitForSingleObject(gSignalEvent, 1000);
+        if (dw == WAIT_TIMEOUT) {
+            /* Do process maintenance */
+            if (SO_LOGROTATE != 0 && --dwRotateCnt == 0) {
+                /* Perform log rotation. */
+
+                 dwRotateCnt = SO_LOGROTATE;
+            }
+            continue;
+        }
+        if (dw == WAIT_OBJECT_0 && gSignalValid) {
+            if (!GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, 0)) {
+                /* Invoke Thread dump */
+                if (gWorker && _jni_startup)
+                    apxJavaDumpAllStacks(gWorker);
+            }
+            ResetEvent(gSignalEvent);
+            continue;
+        }
+        break;
+    }
+    ExitThread(0);
+    return 0;
+}
+
+/* redirect console stdout/stderr to files
+ * so that Java messages can get logged
+ * If stderrfile is not specified it will
+ * go to stdoutfile.
+ */
+static BOOL redirectStdStreams(APX_STDWRAP *lpWrapper, LPAPXCMDLINE lpCmdline)
+{
+    BOOL aErr = FALSE;
+    BOOL aOut = FALSE;
+
+    /* Allocate console if we have none
+     */
+    if (GetConsoleWindow() == NULL) {
+        HWND hc;
+        AllocConsole();
+        if ((hc = GetConsoleWindow()) != NULL)
+            ShowWindow(hc, SW_HIDE);
+    }
+    /* redirect to file or console */
+    if (lpWrapper->szStdOutFilename) {
+        if (lstrcmpiW(lpWrapper->szStdOutFilename, PRSRV_AUTO) == 0) {
+            WCHAR lsn[1024];
+            aOut = TRUE;
+            lstrlcpyW(lsn, 1020, lpCmdline->szApplication);
+            lstrlcatW(lsn, 1020, L"-stdout");
+            lstrlocaseW(lsn);
+            lpWrapper->szStdOutFilename = apxLogFile(gPool,
+                                                     lpWrapper->szLogPath,
+                                                     lsn,
+                                                     NULL, TRUE,
+                                                     SO_LOGROTATE);
+        }
+        /* Delete the file if not in append mode
+         * XXX: See if we can use the params instead of that.
+         */
+        if (!aOut)
+            DeleteFileW(lpWrapper->szStdOutFilename);
+        if ((lpWrapper->fpStdOutFile = _wfopen(lpWrapper->szStdOutFilename,
+                                               L"a"))) {
+            _dup2(_fileno(lpWrapper->fpStdOutFile), 1);
+            *stdout = *lpWrapper->fpStdOutFile;
+            setvbuf(stdout, NULL, _IONBF, 0);
+        }
+        else
+            lpWrapper->szStdOutFilename = NULL;
+    }
+    if (lpWrapper->szStdErrFilename) {
+        if (lstrcmpiW(lpWrapper->szStdErrFilename, PRSRV_AUTO) == 0) {
+            WCHAR lsn[1024];
+            aErr = TRUE;
+            lstrlcpyW(lsn, 1020, lpCmdline->szApplication);
+            lstrlcatW(lsn, 1020, L"-stderr");
+            lstrlocaseW(lsn);
+            lpWrapper->szStdErrFilename = apxLogFile(gPool,
+                                                     lpWrapper->szLogPath,
+                                                     lsn,
+                                                     NULL, TRUE,
+                                                     SO_LOGROTATE);
+        }
+        if (!aErr)
+            DeleteFileW(lpWrapper->szStdErrFilename);
+        if ((lpWrapper->fpStdErrFile = _wfopen(lpWrapper->szStdErrFilename,
+                                              L"a"))) {
+            _dup2(_fileno(lpWrapper->fpStdErrFile), 2);
+            *stderr = *lpWrapper->fpStdErrFile;
+            setvbuf(stderr, NULL, _IONBF, 0);
+        }
+        else
+            lpWrapper->szStdOutFilename = NULL;
+    }
+    else if (lpWrapper->fpStdOutFile) {
+        _dup2(_fileno(lpWrapper->fpStdOutFile), 2);
+        *stderr = *lpWrapper->fpStdOutFile;
+         setvbuf(stderr, NULL, _IONBF, 0);
+    }
+    return TRUE;
+}
+
+/* Debugging functions */
+static void printUsage(LPAPXCMDLINE lpCmdline, BOOL isHelp)
+{
+    int i = 0;
+    fwprintf(stderr, L"Usage: %s command [ServiceName] [--options]\n",
+             lpCmdline->szExecutable);
+    fwprintf(stderr, L"  Commands:\n");
+    if (isHelp)
+        fwprintf(stderr, L"  help                   This page\n");
+    fwprintf(stderr, L"  install [ServiceName]  Install Service\n");
+    fwprintf(stderr, L"  update  [ServiceName]  Update Service parameters\n");
+    fwprintf(stderr, L"  delete  [ServiceName]  Delete Service\n");
+    fwprintf(stderr, L"  start   [ServiceName]  Start Service\n");
+    fwprintf(stderr, L"  stop    [ServiceName]  Stop Service\n");
+    fwprintf(stderr, L"  run     [ServiceName]  Run Service as console application\n");
+    fwprintf(stderr, L"  pause   [Num Seconds]  Sleep for n Seconds (defaults to 60)\n");
+    fwprintf(stderr, L"  version                Display version\n");
+    fwprintf(stderr, L"  Options:\n");
+    while (_options[i].szName) {
+        fwprintf(stderr, L"  --%s\n", _options[i].szName);
+        ++i;
+    }
+}
+
+static void printVersion(void)
+{
+    fwprintf(stderr, L"Apache Commons Daemon Service Runner version %S/Win%d (%S)\n",
+            PRG_VERSION, PRG_BITS, __DATE__);
+    fwprintf(stderr, L"Copyright (c) 2000-2017 The Apache Software Foundation.\n\n"
+                     L"For bug reporting instructions, please see:\n"
+                     L"<URL:https://issues.apache.org/jira/browse/DAEMON>.");
+}
+
+/* Display configuration parameters */
+static void dumpCmdline()
+{
+    int i = 0;
+    while (_options[i].szName) {
+        if (_options[i].dwType & APXCMDOPT_INT)
+            fwprintf(stderr, L"--%-16s %d\n", _options[i].szName,
+                     _options[i].dwValue);
+        else if (_options[i].szValue)
+            fwprintf(stderr, L"--%-16s %s\n", _options[i].szName,
+                     _options[i].szValue);
+        else
+            fwprintf(stderr, L"--%-16s <NULL>\n", _options[i].szName);
+        ++i;
+    }
+}
+
+// TODO: Figure out a way to move apxSetInprocEnvironment from here and
+// prunmgr.c to utils.c
+void apxSetInprocEnvironment()
+{
+    LPWSTR p, e;
+    HMODULE hmodUcrt;
+    WPUTENV wputenv_ucrt;
+
+    if (!SO_ENVIRONMENT)
+        return;    /* Nothing to do */
+
+    hmodUcrt = LoadLibraryExA("ucrtbase.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
+    if (hmodUcrt != NULL) {
+    	wputenv_ucrt =  (WPUTENV) GetProcAddress(hmodUcrt, "_wputenv");
+    }
+
+    for (p = SO_ENVIRONMENT; *p; p++) {
+        e = apxExpandStrW(gPool, p);
+        _wputenv(e);
+        if (wputenv_ucrt != NULL) {
+        	wputenv_ucrt(e);
+        }
+        apxFree(e);
+        while (*p)
+            p++;
+    }
+}
+
+static void setInprocEnvironment9(LPCWSTR szOptions9)
+{
+    DWORD l, c;
+    LPWSTR e, b;
+    LPCWSTR p;
+
+    l = __apxGetMultiSzLengthW(szOptions9, &c);
+
+    if (!c)
+        return;
+
+    /* environment variable name */
+    l += lstrlen(PRSV_JVMOPTS9);
+
+    b = e = apxPoolCalloc(gPool, (l + 1) * sizeof(WCHAR));
+
+    p = PRSV_JVMOPTS9;
+    while (*p) {
+        *b++ = *p++;
+    }
+
+    p = szOptions9;
+    while (c > 0) {
+        if (*p)
+            *b++ = *p;
+        else {
+            *b++ = L' ';
+            c--;
+        }
+        p++;
+    }
+
+    _wputenv(e);
+    apxFree(e);
+}
+
+/* Load the configuration from Registry
+ * loads only nonspecified items
+ */
+static BOOL loadConfiguration(LPAPXCMDLINE lpCmdline)
+{
+    APXHANDLE hRegistry;
+    int i = 0;
+
+    if (!lpCmdline->szApplication) {
+        /* Handle empty service names */
+        apxLogWrite(APXLOG_MARK_WARN "No service name provided");
+        return FALSE;
+    }
+    SetLastError(ERROR_SUCCESS);
+    hRegistry = apxCreateRegistryW(gPool, KEY_READ | KREG_WOW6432,
+                                   PRG_REGROOT,
+                                   lpCmdline->szApplication,
+                                   APXREG_SOFTWARE | APXREG_SERVICE);
+    if (IS_INVALID_HANDLE(hRegistry)) {
+        if (GetLastError() == ERROR_FILE_NOT_FOUND)
+            apxLogWrite(APXLOG_MARK_WARN "The system cannot find the Registry key for service '%S'",
+                        lpCmdline->szApplication);
+        else
+            apxLogWrite(APXLOG_MARK_SYSERR);
+        return FALSE;
+    }
+    /* browse through options */
+    while (_options[i].szName) {
+        DWORD dwFrom;
+
+        dwFrom = (_options[i].dwType & APXCMDOPT_REG) ? APXREG_PARAMSOFTWARE : APXREG_SERVICE;
+        if (!(_options[i].dwType & APXCMDOPT_FOUND)) {
+            if (_options[i].dwType & APXCMDOPT_STR) {
+                _options[i].szValue = apxRegistryGetStringW(hRegistry,
+                                                            dwFrom,
+                                                            _options[i].szSubkey,
+                                                            _options[i].szRegistry);
+                /* Expand environment variables */
+                if (_options[i].szValue && (_options[i].dwType & APXCMDOPT_STE)) {
+                    LPWSTR exp = apxExpandStrW(gPool, _options[i].szValue);
+                    if (exp != _options[i].szValue)
+                        apxFree(_options[i].szValue);
+                    _options[i].szValue = exp;
+                }
+            }
+            else if (_options[i].dwType & APXCMDOPT_INT) {
+                _options[i].dwValue = apxRegistryGetNumberW(hRegistry,
+                                                            dwFrom,
+                                                            _options[i].szSubkey,
+                                                            _options[i].szRegistry);
+            }
+            else if (_options[i].dwType & APXCMDOPT_MSZ) {
+                _options[i].szValue = apxRegistryGetMzStrW(hRegistry,
+                                                           dwFrom,
+                                                           _options[i].szSubkey,
+                                                           _options[i].szRegistry,
+                                                           NULL,
+                                                           &(_options[i].dwValue));
+            }
+        }
+        /* Merge the command line options with registry */
+        else if (_options[i].dwType & APXCMDOPT_ADD) {
+            LPWSTR cv = _options[i].szValue;
+            LPWSTR ov = NULL;
+            if (_options[i].dwType & APXCMDOPT_MSZ) {
+                ov = apxRegistryGetMzStrW(hRegistry, dwFrom,
+                                          _options[i].szSubkey,
+                                          _options[i].szRegistry,
+                                          NULL,
+                                          &(_options[i].dwValue));
+                _options[i].szValue = apxMultiSzCombine(gPool, ov, cv,
+                                                        &(_options[i].dwValue));
+                if (ov)
+                    apxFree(ov);
+            }
+        }
+        ++i;
+    }
+    apxCloseHandle(hRegistry);
+#ifdef _DEBUG
+    dumpCmdline();
+#endif
+    return TRUE;
+}
+
+/* Save changed configuration to registry
+ */
+static BOOL saveConfiguration(LPAPXCMDLINE lpCmdline)
+{
+    APXHANDLE hRegistry;
+    int i = 0;
+    hRegistry = apxCreateRegistryW(gPool, KEY_WRITE | KREG_WOW6432,
+                                   PRG_REGROOT,
+                                   lpCmdline->szApplication,
+                                   APXREG_SOFTWARE | APXREG_SERVICE);
+    if (IS_INVALID_HANDLE(hRegistry))
+        return FALSE;
+    /* TODO: Use array size */
+    while (_options[i].szName) {
+        /* Skip the service params */
+        if ((_options[i].dwType & APXCMDOPT_SRV) ||
+            !(_options[i].dwType & APXCMDOPT_FOUND)) {
+                /* Skip non-modified version */
+        }
+        /* Update only modified params */
+        else if (_options[i].dwType & APXCMDOPT_STR)
+            apxRegistrySetStrW(hRegistry, APXREG_PARAMSOFTWARE,
+                               _options[i].szSubkey,
+                               _options[i].szRegistry,
+                               _options[i].szValue);
+        else if (_options[i].dwType & APXCMDOPT_INT)
+            apxRegistrySetNumW(hRegistry, APXREG_PARAMSOFTWARE,
+                               _options[i].szSubkey,
+                               _options[i].szRegistry,
+                               _options[i].dwValue);
+        else if (_options[i].dwType & APXCMDOPT_MSZ)
+            apxRegistrySetMzStrW(hRegistry, APXREG_PARAMSOFTWARE,
+                                 _options[i].szSubkey,
+                                 _options[i].szRegistry,
+                                 _options[i].szValue,
+                                 _options[i].dwValue);
+        ++i;
+    }
+    apxCloseHandle(hRegistry);
+    return TRUE;
+}
+
+/* Operations */
+static BOOL docmdInstallService(LPAPXCMDLINE lpCmdline)
+{
+    APXHANDLE hService;
+    BOOL  rv;
+    DWORD dwStart = SERVICE_DEMAND_START;
+    DWORD dwType  = SERVICE_WIN32_OWN_PROCESS;
+    WCHAR szImage[SIZ_HUGLEN];
+    WCHAR szName[SIZ_BUFLEN];
+
+    apxLogWrite(APXLOG_MARK_DEBUG "Installing service...");
+    hService = apxCreateService(gPool, SC_MANAGER_CREATE_SERVICE, FALSE);
+    if (IS_INVALID_HANDLE(hService)) {
+        apxLogWrite(APXLOG_MARK_ERROR "Unable to open the Service Manager");
+        return FALSE;
+    }
+    /* Check the startup mode */
+    if ((ST_STARTUP & APXCMDOPT_FOUND) &&
+        lstrcmpiW(SO_STARTUP, PRSRV_AUTO) == 0)
+        dwStart = SERVICE_AUTO_START;
+    /* Check the service type */
+    if ((ST_TYPE & APXCMDOPT_FOUND) &&
+        lstrcmpiW(SO_TYPE, STYPE_INTERACTIVE) == 0)
+        dwType |= SERVICE_INTERACTIVE_PROCESS;
+
+    /* Check if --Install is provided */
+    if (!IS_VALID_STRING(SO_INSTALL)) {
+        lstrlcpyW(szImage, SIZ_HUGLEN, lpCmdline->szExePath);
+        lstrlcatW(szImage, SIZ_HUGLEN, L"\\");
+        lstrlcatW(szImage, SIZ_HUGLEN, lpCmdline->szExecutable);
+        lstrlcatW(szImage, SIZ_HUGLEN, L".exe");
+    }
+    else
+        lstrlcpyW(szImage, SIZ_HUGLEN, SO_INSTALL);
+    /* Replace not needed quotes */
+    apxStrQuoteInplaceW(szImage);
+    /* Add run-service command line option */
+    lstrlcatW(szImage, SIZ_HUGLEN, L" ");
+    lstrlcpyW(szName, SIZ_BUFLEN, L"//RS//");
+    lstrlcatW(szName, SIZ_BUFLEN, lpCmdline->szApplication);
+    apxStrQuoteInplaceW(szName);
+    lstrlcatW(szImage, SIZ_HUGLEN, szName);
+    SO_INSTALL = apxPoolStrdupW(gPool, szImage);
+    /* Ensure that option gets saved in the registry */
+    ST_INSTALL |= APXCMDOPT_FOUND;
+#ifdef _DEBUG
+    /* Display configured options */
+    dumpCmdline();
+#endif
+    apxLogWrite(APXLOG_MARK_INFO "Service %S name %S", lpCmdline->szApplication,
+                SO_DISPLAYNAME);
+    rv = apxServiceInstall(hService,
+                          lpCmdline->szApplication,
+                          SO_DISPLAYNAME,    /* --DisplayName  */
+                          SO_INSTALL,
+                          SO_DEPENDSON,      /* --DependendsOn */
+                          dwType,
+                          dwStart);
+    /* Set the --Description */
+    if (rv) {
+        LPCWSTR sd = NULL;
+        LPCWSTR su = NULL;
+        LPCWSTR sp = NULL;
+        if (ST_DESCRIPTION & APXCMDOPT_FOUND) {
+            sd = SO_DESCRIPTION;
+            apxLogWrite(APXLOG_MARK_DEBUG "Setting service description %S",
+                        SO_DESCRIPTION);
+        }
+        if (ST_SUSER & APXCMDOPT_FOUND) {
+            su = SO_SUSER;
+            apxLogWrite(APXLOG_MARK_DEBUG "Setting service user %S",
+                        SO_SUSER);
+        }
+        if (ST_SPASSWORD & APXCMDOPT_FOUND) {
+            sp = SO_SPASSWORD;
+            apxLogWrite(APXLOG_MARK_DEBUG "Setting service password %S",
+                        SO_SPASSWORD);
+        }
+        apxServiceSetNames(hService, NULL, NULL, sd, su, sp);
+    }
+    apxCloseHandle(hService);
+    if (rv) {
+        saveConfiguration(lpCmdline);
+        apxLogWrite(APXLOG_MARK_INFO "Service '%S' installed",
+                    lpCmdline->szApplication);
+    }
+    else
+        apxLogWrite(APXLOG_MARK_ERROR "Failed installing '%S' service",
+                    lpCmdline->szApplication);
+
+    return rv;
+}
+
+static BOOL docmdDeleteService(LPAPXCMDLINE lpCmdline)
+{
+    APXHANDLE hService;
+    BOOL  rv = FALSE;
+
+    apxLogWrite(APXLOG_MARK_INFO "Deleting service...");
+    hService = apxCreateService(gPool, SC_MANAGER_CONNECT, FALSE);
+    if (IS_INVALID_HANDLE(hService)) {
+        apxLogWrite(APXLOG_MARK_ERROR "Unable to open the Service Manager");
+        return FALSE;
+    }
+    /* Delete service will stop the service if running */
+    if (apxServiceOpen(hService, lpCmdline->szApplication, SERVICE_ALL_ACCESS)) {
+        WCHAR szWndManagerClass[SIZ_RESLEN];
+        HANDLE hWndManager = NULL;
+        lstrlcpyW(szWndManagerClass, SIZ_RESLEN, lpCmdline->szApplication);
+        lstrlcatW(szWndManagerClass, SIZ_RESLEN, L"_CLASS");
+        /* Close the monitor application if running */
+        if ((hWndManager = FindWindowW(szWndManagerClass, NULL)) != NULL) {
+            SendMessage(hWndManager, WM_CLOSE, 0, 0);
+        }
+        rv = apxServiceDelete(hService);
+    }
+    if (rv) {
+        /* Delete all service registry settings */
+        apxDeleteRegistryW(PRG_REGROOT, lpCmdline->szApplication, KREG_WOW6432, TRUE);
+        apxLogWrite(APXLOG_MARK_DEBUG "Service '%S' deleted",
+                    lpCmdline->szApplication);
+    }
+    else {
+        apxDisplayError(FALSE, NULL, 0, "Unable to delete '%S' service",
+                        lpCmdline->szApplication);
+    }
+    apxCloseHandle(hService);
+    apxLogWrite(APXLOG_MARK_INFO "Delete service finished.");
+    return rv;
+}
+
+static BOOL docmdStopService(LPAPXCMDLINE lpCmdline)
+{
+    APXHANDLE hService;
+    BOOL  rv = FALSE;
+
+    apxLogWrite(APXLOG_MARK_INFO "Stopping service '%S' ...",
+                lpCmdline->szApplication);
+    hService = apxCreateService(gPool, GENERIC_ALL, FALSE);
+    if (IS_INVALID_HANDLE(hService)) {
+        apxLogWrite(APXLOG_MARK_ERROR "Unable to open the Service Manager");
+        return FALSE;
+    }
+
+    SetLastError(ERROR_SUCCESS);
+    /* Open the service */
+    if (apxServiceOpen(hService, lpCmdline->szApplication,
+                       GENERIC_READ | GENERIC_EXECUTE)) {
+        rv = apxServiceControl(hService,
+                               SERVICE_CONTROL_STOP,
+                               0,
+                               NULL,
+                               NULL);
+        if (rv)
+            apxLogWrite(APXLOG_MARK_INFO "Service '%S' stopped",
+                        lpCmdline->szApplication);
+        else
+            apxLogWrite(APXLOG_MARK_ERROR "Failed to stop '%S' service",
+                        lpCmdline->szApplication);
+
+    }
+    else
+        apxDisplayError(FALSE, NULL, 0, "Unable to open '%S' service",
+                        lpCmdline->szApplication);
+    apxCloseHandle(hService);
+    apxLogWrite(APXLOG_MARK_INFO "Stop service finished.");
+    return rv;
+}
+
+static BOOL docmdStartService(LPAPXCMDLINE lpCmdline)
+{
+    APXHANDLE hService;
+    BOOL  rv = FALSE;
+
+    apxLogWrite(APXLOG_MARK_INFO "Starting service '%S' ...",
+                lpCmdline->szApplication);
+    hService = apxCreateService(gPool, GENERIC_ALL, FALSE);
+    if (IS_INVALID_HANDLE(hService)) {
+        apxLogWrite(APXLOG_MARK_ERROR "Unable to open the Service Manager");
+        return FALSE;
+    }
+
+    SetLastError(ERROR_SUCCESS);
+    /* Open the service */
+    if (apxServiceOpen(hService, lpCmdline->szApplication,
+                       GENERIC_READ | GENERIC_EXECUTE)) {
+        rv = apxServiceControl(hService,
+                               SERVICE_CONTROL_CONTINUE,
+                               0,
+                               NULL,
+                               NULL);
+        if (rv)
+            apxLogWrite(APXLOG_MARK_INFO "Started service '%S'",
+                        lpCmdline->szApplication);
+        else
+            apxLogWrite(APXLOG_MARK_ERROR "Failed to start service '%S'",
+                        lpCmdline->szApplication);
+
+    }
+    else
+        apxDisplayError(FALSE, NULL, 0, "Unable to open service '%S'",
+                        lpCmdline->szApplication);
+    apxCloseHandle(hService);
+    apxLogWrite(APXLOG_MARK_INFO "Start service finished, returning %d", rv);
+    return rv;
+}
+
+static BOOL docmdUpdateService(LPAPXCMDLINE lpCmdline)
+{
+    APXHANDLE hService;
+    BOOL  rv = TRUE;
+
+    apxLogWrite(APXLOG_MARK_INFO "Updating service...");
+
+    hService = apxCreateService(gPool, SC_MANAGER_CREATE_SERVICE, FALSE);
+    if (IS_INVALID_HANDLE(hService)) {
+        apxLogWrite(APXLOG_MARK_ERROR "Unable to open the Service Manager");
+        return FALSE;
+    }
+    SetLastError(0);
+    /* Open the service */
+    if (!apxServiceOpen(hService, lpCmdline->szApplication, SERVICE_ALL_ACCESS)) {
+        /* Close the existing manager handler.
+         * It will be reopened inside install.
+         */
+        apxCloseHandle(hService);
+        /* In case service doesn't exist try to install it.
+         * Install will fail if there is no minimum parameters required.
+         */
+        return docmdInstallService(lpCmdline);
+    }
+    else {
+        DWORD dwStart = SERVICE_NO_CHANGE;
+        DWORD dwType  = SERVICE_NO_CHANGE;
+        LPCWSTR su = NULL;
+        LPCWSTR sp = NULL;
+        if (ST_SUSER & APXCMDOPT_FOUND) {
+            su = SO_SUSER;
+            apxLogWrite(APXLOG_MARK_DEBUG "Setting service user %S",
+                        SO_SUSER);
+        }
+        if (ST_SPASSWORD & APXCMDOPT_FOUND) {
+            sp = SO_SPASSWORD;
+            apxLogWrite(APXLOG_MARK_DEBUG "Setting service password %S",
+                        SO_SPASSWORD);
+        }
+        rv = (rv && apxServiceSetNames(hService,
+                                       NULL,                /* Never update the ImagePath */
+                                       SO_DISPLAYNAME,
+                                       SO_DESCRIPTION,
+                                       su,
+                                       sp));
+        /* Update the --Startup mode */
+        if (ST_STARTUP & APXCMDOPT_FOUND) {
+            if (!lstrcmpiW(SO_STARTUP, PRSRV_AUTO))
+                dwStart = SERVICE_AUTO_START;
+            else if (!lstrcmpiW(SO_STARTUP, PRSRV_MANUAL))
+                dwStart = SERVICE_DEMAND_START;
+        }
+        if (ST_TYPE & APXCMDOPT_FOUND) {
+            if (!lstrcmpiW(SO_TYPE, STYPE_INTERACTIVE))
+                dwType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
+        }
+        rv = (rv && apxServiceSetOptions(hService,
+                                         dwType,
+                                         dwStart,
+                                         SERVICE_NO_CHANGE));
+
+        apxLogWrite(APXLOG_MARK_INFO "Service '%S' updated",
+                    lpCmdline->szApplication);
+
+        rv = (rv && saveConfiguration(lpCmdline));
+    }
+    apxCloseHandle(hService);
+    if (rv)
+        apxLogWrite(APXLOG_MARK_INFO "Update service finished.");
+    else
+        apxLogWrite(APXLOG_MARK_INFO "Update service '%S' failed.",
+                                     lpCmdline->szApplication);
+    return rv;
+}
+
+
+/* Report the service status to the SCM, including service specific exit code
+ */
+static BOOL reportServiceStatusE(DWORD dwCurrentState,
+                                 DWORD dwWin32ExitCode,
+                                 DWORD dwWaitHint,
+                                 DWORD dwServiceSpecificExitCode)
+{
+   static DWORD dwCheckPoint = 1;
+   BOOL fResult = TRUE;
+
+   apxLogWrite(APXLOG_MARK_DEBUG "reportServiceStatusE: dwCurrentState = %d, dwWin32ExitCode = %d, dwWaitHint = %d, dwServiceSpecificExitCode = %d",
+               dwCurrentState, dwWin32ExitCode, dwWaitHint, dwServiceSpecificExitCode);
+
+   if (_service_mode && _service_status_handle) {
+       if (dwCurrentState == SERVICE_RUNNING)
+            _service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
+        else
+            _service_status.dwControlsAccepted = 0;
+
+       _service_status.dwCurrentState  = dwCurrentState;
+       _service_status.dwWin32ExitCode = dwWin32ExitCode;
+       _service_status.dwWaitHint      = dwWaitHint;
+       _service_status.dwServiceSpecificExitCode = dwServiceSpecificExitCode;
+
+       if ((dwCurrentState == SERVICE_RUNNING) ||
+           (dwCurrentState == SERVICE_STOPPED))
+           _service_status.dwCheckPoint = 0;
+       else
+           _service_status.dwCheckPoint = dwCheckPoint++;
+       fResult = SetServiceStatus(_service_status_handle, &_service_status);
+       if (!fResult) {
+           /* TODO: Deal with error */
+           apxLogWrite(APXLOG_MARK_ERROR "Failed to set service status");
+       }
+   }
+   return fResult;
+}
+
+/* Report the service status to the SCM
+ */
+static BOOL reportServiceStatus(DWORD dwCurrentState,
+                                DWORD dwWin32ExitCode,
+                                DWORD dwWaitHint)
+{
+    return reportServiceStatusE(dwCurrentState, dwWin32ExitCode, dwWaitHint, 0);
+}
+
+static BOOL reportServiceStatusStopped(DWORD exitCode)
+{
+    if (exitCode) {
+        return reportServiceStatusE(SERVICE_STOPPED, ERROR_SERVICE_SPECIFIC_ERROR, 0, exitCode);
+    } else {
+        return reportServiceStatus(SERVICE_STOPPED, NO_ERROR, 0);
+    }
+}
+
+BOOL child_callback(APXHANDLE hObject, UINT uMsg,
+                    WPARAM wParam, LPARAM lParam)
+{
+    /* TODO: Make stdout and stderr buffers
+     * to prevent streams intermixing when there
+     * is no separate file for each stream
+     */
+    if (uMsg == WM_CHAR) {
+        int ch = LOWORD(wParam);
+        if (lParam)
+            fputc(ch, stderr);
+        else
+            fputc(ch, stdout);
+    }
+    return TRUE;
+}
+
+static int onExitStop(void)
+{
+    if (_service_mode) {
+        apxLogWrite(APXLOG_MARK_DEBUG "Stop exit hook called ...");
+        reportServiceStatusStopped(0);
+    }
+    return 0;
+}
+
+static int onExitStart(void)
+{
+    if (_service_mode) {
+        apxLogWrite(APXLOG_MARK_DEBUG "Start exit hook called ...");
+        apxLogWrite(APXLOG_MARK_DEBUG "VM exit code: %d", apxGetVmExitCode());
+        /* Reporting the service as stopped even with a non-zero exit code
+         * will not cause recovery actions to be initiated, so don't report at all.
+         * "A service is considered failed when it terminates without reporting a
+         * status of SERVICE_STOPPED to the service controller"
+         * http://msdn.microsoft.com/en-us/library/ms685939(VS.85).aspx
+         */
+        if (apxGetVmExitCode() == 0) {
+            reportServiceStatusStopped(0);
+        }
+    }
+    return 0;
+}
+
+/* Executed when the service receives stop event */
+static DWORD WINAPI serviceStop(LPVOID lpParameter)
+{
+    APXHANDLE hWorker = NULL;
+    DWORD  rv = 0;
+    BOOL   wait_to_die = FALSE;
+    DWORD  timeout     = SO_STOPTIMEOUT * 1000;
+    DWORD  dwCtrlType  = (DWORD)((BYTE *)lpParameter - (BYTE *)0);
+
+    apxLogWrite(APXLOG_MARK_INFO "Stopping service...");
+
+    if (IS_INVALID_HANDLE(gWorker)) {
+        apxLogWrite(APXLOG_MARK_INFO "Worker is not defined");
+        return TRUE;    /* Nothing to do */
+    }
+    if (_jni_shutdown) {
+        if (!IS_VALID_STRING(SO_STARTPATH) && IS_VALID_STRING(SO_STOPPATH)) {
+            /* If the Working path is specified change the current directory
+             * but only if the start path wasn't specified already.
+             */
+            SetCurrentDirectoryW(SO_STOPPATH);
+        }
+        hWorker = apxCreateJava(gPool, _jni_jvmpath);
+        if (IS_INVALID_HANDLE(hWorker)) {
+            apxLogWrite(APXLOG_MARK_ERROR "Failed creating Java %S", _jni_jvmpath);
+            return 1;
+        }
+        gSargs.hJava            = hWorker;
+        gSargs.szClassPath      = _jni_classpath;
+        gSargs.lpOptions        = _jni_jvmoptions;
+        gSargs.lpOptions9       = _jni_jvmoptions9;
+        gSargs.dwMs             = SO_JVMMS;
+        gSargs.dwMx             = SO_JVMMX;
+        gSargs.dwSs             = SO_JVMSS;
+        gSargs.bJniVfprintf     = SO_JNIVFPRINTF;
+        gSargs.szClassName      = _jni_sclass;
+        gSargs.szMethodName     = _jni_smethod;
+        gSargs.lpArguments      = _jni_sparam;
+        gSargs.szStdErrFilename = NULL;
+        gSargs.szStdOutFilename = NULL;
+        gSargs.szLibraryPath    = SO_LIBPATH;
+        /* Register onexit hook
+         */
+        _onexit(onExitStop);
+        /* Create shutdown event */
+        gShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+        if (!apxJavaStart(&gSargs)) {
+            apxLogWrite(APXLOG_MARK_ERROR "Failed starting Java");
+            rv = 3;
+        }
+        else {
+            if (lstrcmpA(_jni_sclass, "java/lang/System") == 0) {
+                reportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, 20 * 1000);
+                apxLogWrite(APXLOG_MARK_DEBUG "Forcing Java JNI System.exit worker to finish...");
+                return 0;
+            }
+            else {
+                apxLogWrite(APXLOG_MARK_DEBUG "Waiting for Java JNI stop worker to finish...");
+                apxJavaWait(hWorker, INFINITE, FALSE);
+                apxLogWrite(APXLOG_MARK_DEBUG "Java JNI stop worker finished.");
+            }
+        }
+        wait_to_die = TRUE;
+    }
+    else if (IS_VALID_STRING(SO_STOPMODE)) { /* Only in case we have a stop mode */
+        DWORD nArgs;
+        LPWSTR *pArgs;
+
+        if (!IS_VALID_STRING(SO_STOPIMAGE)) {
+            apxLogWrite(APXLOG_MARK_ERROR "Missing service ImageFile");
+            if (!_service_mode)
+                apxDisplayError(FALSE, NULL, 0, "Service '%S' is missing the ImageFile",
+                                _service_name ? _service_name : L"unknown");
+            return 1;
+        }
+        /* Redirect process */
+        hWorker = apxCreateProcessW(gPool,
+                                    0,
+                                    child_callback,
+                                    SO_USER,
+                                    SO_PASSWORD,
+                                    FALSE);
+        if (IS_INVALID_HANDLE(hWorker)) {
+            apxLogWrite(APXLOG_MARK_ERROR "Failed creating process");
+            return 1;
+        }
+        /* If the service process completes before the stop process does the
+         * cleanup code below will free structures required by the stop process
+         * which will, in all probability, trigger a crash. Wait for the stop
+         * process to complete before cleaning up.
+         */
+        gShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+        if (!apxProcessSetExecutableW(hWorker, SO_STOPIMAGE)) {
+            apxLogWrite(APXLOG_MARK_ERROR "Failed setting process executable %S",
+                        SO_STOPIMAGE);
+            rv = 2;
+            goto cleanup;
+        }
+        /* Assemble the command line */
+        if (_java_shutdown) {
+            nArgs = apxJavaCmdInitialize(gPool, SO_CLASSPATH, SO_STOPCLASS,
+                                         SO_JVMOPTIONS, SO_JVMMS, SO_JVMMX,
+                                         SO_JVMSS, SO_STOPPARAMS, &pArgs);
+        }
+        else {
+            nArgs = apxMultiSzToArrayW(gPool, SO_STOPPARAMS, &pArgs);
+        }
+
+        /* Pass the argv to child process */
+        if (!apxProcessSetCommandArgsW(hWorker, SO_STOPIMAGE,
+                                       nArgs, pArgs)) {
+            rv = 3;
+            apxLogWrite(APXLOG_MARK_ERROR "Failed setting process arguments (argc=%d)",
+                        nArgs);
+            goto cleanup;
+        }
+        /* Set the working path */
+        if (!apxProcessSetWorkingPathW(hWorker, SO_STOPPATH)) {
+            rv = 4;
+            apxLogWrite(APXLOG_MARK_ERROR "Failed setting process working path to %S",
+                        SO_STOPPATH);
+            goto cleanup;
+        }
+        /* Finally execute the child process
+         */
+        if (!apxProcessExecute(hWorker)) {
+            rv = 5;
+            apxLogWrite(APXLOG_MARK_ERROR "Failed executing process");
+            goto cleanup;
+        } else {
+            apxLogWrite(APXLOG_MARK_DEBUG "Waiting for stop worker to finish...");
+            apxHandleWait(hWorker, INFINITE, FALSE);
+            apxLogWrite(APXLOG_MARK_DEBUG "Stop worker finished.");
+        }
+        wait_to_die = TRUE;
+    }
+cleanup:
+    /* Close Java JNI handle or stop worker
+     * If this is the single JVM instance it will unload
+     * the JVM dll too.
+     * The worker will be closed on service exit.
+     */
+    if (!IS_INVALID_HANDLE(hWorker))
+        apxCloseHandle(hWorker);
+    if (gSignalEvent) {
+        gSignalValid = FALSE;
+        SetEvent(gSignalEvent);
+        WaitForSingleObject(gSignalThread, 1000);
+        CloseHandle(gSignalEvent);
+        CloseHandle(gSignalThread);
+        gSignalEvent = NULL;
+    }
+    if (timeout > 0x7FFFFFFF)
+        timeout = INFINITE;     /* If the timeout was '-1' wait forewer */
+    if (wait_to_die && !timeout)
+        timeout = 300 * 1000;   /* Use the 5 minute default shutdown */
+
+    if (dwCtrlType == SERVICE_CONTROL_SHUTDOWN)
+        timeout = MIN(timeout, apxGetMaxServiceTimeout(gPool));
+    reportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, timeout);
+
+    if (timeout) {
+        FILETIME fts, fte;
+        ULARGE_INTEGER s, e;
+        DWORD    nms;
+        /* Wait to give it a chance to die naturally, then kill it. */
+        apxLogWrite(APXLOG_MARK_DEBUG "Waiting for worker to die naturally...");
+        GetSystemTimeAsFileTime(&fts);
+        rv = apxHandleWait(gWorker, timeout, TRUE);
+        GetSystemTimeAsFileTime(&fte);
+        s.LowPart  = fts.dwLowDateTime;
+        s.HighPart = fts.dwHighDateTime;
+        e.LowPart  = fte.dwLowDateTime;
+        e.HighPart = fte.dwHighDateTime;
+        nms = (DWORD)((e.QuadPart - s.QuadPart) / 10000);
+        if (rv == WAIT_OBJECT_0) {
+            rv = 0;
+            apxLogWrite(APXLOG_MARK_DEBUG "Worker finished gracefully in %d ms.", nms);
+        }
+        else
+            apxLogWrite(APXLOG_MARK_DEBUG "Worker was killed in %d ms.", nms);
+    }
+    else {
+        apxLogWrite(APXLOG_MARK_DEBUG "Sending WM_CLOSE to worker");
+        apxHandleSendMessage(gWorker, WM_CLOSE, 0, 0);
+    }
+
+    apxLogWrite(APXLOG_MARK_INFO "Service stop thread completed.");
+    if (gShutdownEvent) {
+        SetEvent(gShutdownEvent);
+    }
+    return rv;
+}
+
+/* Executed when the service receives start event */
+static DWORD serviceStart()
+{
+    DWORD  rv = 0;
+    DWORD  nArgs;
+    LPWSTR *pArgs;
+    FILETIME fts;
+
+    apxLogWrite(APXLOG_MARK_INFO "Starting service...");
+
+    if (!IS_INVALID_HANDLE(gWorker)) {
+        apxLogWrite(APXLOG_MARK_INFO "Worker is not defined");
+        return TRUE;    /* Nothing to do */
+    }
+    if (IS_VALID_STRING(SO_PIDFILE)) {
+        gPidfileName = apxLogFile(gPool, SO_LOGPATH, SO_PIDFILE, NULL, FALSE, 0);
+        if (GetFileAttributesW(gPidfileName) !=  INVALID_FILE_ATTRIBUTES) {
+            /* Pid file exists */
+            if (!DeleteFileW(gPidfileName)) {
+                /* Delete failed. Either no access or opened */
+                apxLogWrite(APXLOG_MARK_ERROR "Pid file '%S' exists",
+                            gPidfileName);
+                return 1;
+            }
+        }
+    }
+    GetSystemTimeAsFileTime(&fts);
+    if (_jni_startup) {
+        if (IS_EMPTY_STRING(SO_STARTPATH))
+            SO_STARTPATH = gStartPath;
+        if (IS_VALID_STRING(SO_STARTPATH)) {
+            /* If the Working path is specified change the current directory */
+            SetCurrentDirectoryW(SO_STARTPATH);
+        }
+        if (IS_VALID_STRING(SO_LIBPATH)) {
+            /* Add LibraryPath to the PATH */
+           apxAddToPathW(gPool, SO_LIBPATH);
+        }
+        /* Create the JVM global worker */
+        gWorker = apxCreateJava(gPool, _jni_jvmpath);
+        if (IS_INVALID_HANDLE(gWorker)) {
+            apxLogWrite(APXLOG_MARK_ERROR "Failed creating Java %S", _jni_jvmpath);
+            return 1;
+        }
+        gRargs.hJava            = gWorker;
+        gRargs.szClassPath      = _jni_classpath;
+        gRargs.lpOptions        = _jni_jvmoptions;
+        gRargs.lpOptions9       = _jni_jvmoptions9;
+        gRargs.dwMs             = SO_JVMMS;
+        gRargs.dwMx             = SO_JVMMX;
+        gRargs.dwSs             = SO_JVMSS;
+        gRargs.bJniVfprintf     = SO_JNIVFPRINTF;
+        gRargs.szClassName      = _jni_rclass;
+        gRargs.szMethodName     = _jni_rmethod;
+        gRargs.lpArguments      = _jni_rparam;
+        gRargs.szStdErrFilename = gStdwrap.szStdErrFilename;
+        gRargs.szStdOutFilename = gStdwrap.szStdOutFilename;
+        gRargs.szLibraryPath    = SO_LIBPATH;
+        /* Register onexit hook
+         */
+        _onexit(onExitStart);
+        if (!apxJavaStart(&gRargs)) {
+            rv = 4;
+            apxLogWrite(APXLOG_MARK_ERROR "Failed to start Java");
+            goto cleanup;
+        }
+        apxLogWrite(APXLOG_MARK_DEBUG "Java started %s", _jni_rclass);
+    }
+    else {
+        if (!IS_VALID_STRING(SO_STARTIMAGE)) {
+            apxLogWrite(APXLOG_MARK_ERROR "Missing service ImageFile");
+            if (!_service_mode)
+                apxDisplayError(FALSE, NULL, 0, "Service '%S' is missing the ImageFile",
+                                _service_name ? _service_name : L"unknown");
+            return 1;
+        }
+        if (IS_VALID_STRING(SO_LIBPATH)) {
+            /* Add LibraryPath to the PATH */
+           apxAddToPathW(gPool, SO_LIBPATH);
+        }
+        /* Set the environment using putenv, so JVM can use it */
+        apxSetInprocEnvironment();
+        setInprocEnvironment9(SO_JVMOPTIONS9);
+        /* Redirect process */
+        gWorker = apxCreateProcessW(gPool,
+                                    0,
+                                    child_callback,
+                                    SO_USER,
+                                    SO_PASSWORD,
+                                    FALSE);
+        if (IS_INVALID_HANDLE(gWorker)) {
+            apxLogWrite(APXLOG_MARK_ERROR "Failed to create process");
+            return 1;
+        }
+        if (!apxProcessSetExecutableW(gWorker, SO_STARTIMAGE)) {
+            apxLogWrite(APXLOG_MARK_ERROR "Failed setting process executable %S",
+                        SO_STARTIMAGE);
+            rv = 2;
+            goto cleanup;
+        }
+        /* Assemble the command line */
+        if (_java_startup) {
+            nArgs = apxJavaCmdInitialize(gPool, SO_CLASSPATH, SO_STARTCLASS,
+                                         SO_JVMOPTIONS, SO_JVMMS, SO_JVMMX,
+                                         SO_JVMSS, SO_STARTPARAMS, &pArgs);
+        }
+        else {
+            nArgs = apxMultiSzToArrayW(gPool, SO_STARTPARAMS, &pArgs);
+        }
+
+        /* Pass the argv to child process */
+        if (!apxProcessSetCommandArgsW(gWorker, SO_STARTIMAGE,
+                                       nArgs, pArgs)) {
+            rv = 3;
+            apxLogWrite(APXLOG_MARK_ERROR "Failed setting process arguments (argc=%d)",
+                        nArgs);
+            goto cleanup;
+        }
+        /* Set the working path */
+        if (!apxProcessSetWorkingPathW(gWorker, SO_STARTPATH)) {
+            rv = 4;
+            apxLogWrite(APXLOG_MARK_ERROR "Failed setting process working path to %S",
+                        SO_STARTPATH);
+            goto cleanup;
+        }
+        /* Finally execute the child process
+         */
+        if (!apxProcessExecute(gWorker)) {
+            rv = 5;
+            apxLogWrite(APXLOG_MARK_ERROR "Failed to execute process");
+            goto cleanup;
+        }
+    }
+    if (rv == 0) {
+        FILETIME fte;
+        ULARGE_INTEGER s, e;
+        DWORD    nms;
+        /* Create pidfile */
+        if (gPidfileName) {
+            char pids[32];
+            gPidfileHandle = CreateFileW(gPidfileName,
+                                         GENERIC_READ | GENERIC_WRITE,
+                                         FILE_SHARE_READ,
+                                         NULL,
+                                         CREATE_NEW,
+                                         FILE_ATTRIBUTE_NORMAL,
+                                         NULL);
+
+            if (gPidfileHandle != INVALID_HANDLE_VALUE) {
+                DWORD wr = 0;
+                if (_jni_startup)
+                    _snprintf(pids, 32, "%d\r\n", GetCurrentProcessId());
+                else
+                    _snprintf(pids, 32, "%d\r\n", apxProcessGetPid(gWorker));
+                WriteFile(gPidfileHandle, pids, (DWORD)strlen(pids), &wr, NULL);
+                FlushFileBuffers(gPidfileName);
+            }
+        }
+        GetSystemTimeAsFileTime(&fte);
+        s.LowPart  = fts.dwLowDateTime;
+        s.HighPart = fts.dwHighDateTime;
+        e.LowPart  = fte.dwLowDateTime;
+        e.HighPart = fte.dwHighDateTime;
+        nms = (DWORD)((e.QuadPart - s.QuadPart) / 10000);
+        apxLogWrite(APXLOG_MARK_INFO "Service started in %d ms.", nms);
+    }
+    return rv;
+cleanup:
+    if (!IS_INVALID_HANDLE(gWorker))
+        apxCloseHandle(gWorker);    /* Close the worker handle */
+    gWorker = NULL;
+    return rv;
+}
+
+/* Service control handler
+ */
+void WINAPI service_ctrl_handler(DWORD dwCtrlCode)
+{
+    DWORD  threadId;
+    HANDLE stopThread;
+
+    switch (dwCtrlCode) {
+        case SERVICE_CONTROL_SHUTDOWN:
+            apxLogWrite(APXLOG_MARK_INFO "Service SHUTDOWN signalled");
+        case SERVICE_CONTROL_STOP:
+            if (SO_STOPTIMEOUT > 0) {
+                reportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, SO_STOPTIMEOUT * 1000);
+            }
+            else {
+                reportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, 3 * 1000);
+            }
+            /* Stop the service asynchronously */
+            stopThread = CreateThread(NULL, 0,
+                                      serviceStop,
+                                      (LPVOID)dwCtrlCode,
+                                      0, &threadId);
+            CloseHandle(stopThread);
+            return;
+        case SERVICE_CONTROL_INTERROGATE:
+            reportServiceStatus(_service_status.dwCurrentState,
+                                _service_status.dwWin32ExitCode,
+                                _service_status.dwWaitHint);
+            return;
+        default:
+            break;
+   }
+}
+
+/* Console control handler
+ *
+ */
+BOOL WINAPI console_handler(DWORD dwCtrlType)
+{
+    switch (dwCtrlType) {
+        case CTRL_BREAK_EVENT:
+            apxLogWrite(APXLOG_MARK_INFO "Console CTRL+BREAK event signaled");
+            return FALSE;
+        case CTRL_C_EVENT:
+            apxLogWrite(APXLOG_MARK_INFO "Console CTRL+C event signaled");
+            serviceStop((LPVOID)SERVICE_CONTROL_STOP);
+            return TRUE;
+        case CTRL_CLOSE_EVENT:
+            apxLogWrite(APXLOG_MARK_INFO "Console CTRL+CLOSE event signaled");
+            serviceStop((LPVOID)SERVICE_CONTROL_STOP);
+            return TRUE;
+        case CTRL_SHUTDOWN_EVENT:
+            apxLogWrite(APXLOG_MARK_INFO "Console SHUTDOWN event signaled");
+            serviceStop((LPVOID)SERVICE_CONTROL_SHUTDOWN);
+            return TRUE;
+        case CTRL_LOGOFF_EVENT:
+            apxLogWrite(APXLOG_MARK_INFO "Console LOGOFF event signaled");
+            if (!_service_mode) {
+                serviceStop((LPVOID)SERVICE_CONTROL_STOP);
+            }
+            return TRUE;
+        break;
+
+   }
+   return FALSE;
+}
+
+/* Main service execution loop */
+void WINAPI serviceMain(DWORD argc, LPTSTR *argv)
+{
+    DWORD rc = 0;
+    _service_status.dwServiceType      = SERVICE_WIN32_OWN_PROCESS;
+    _service_status.dwCurrentState     = SERVICE_START_PENDING;
+    _service_status.dwControlsAccepted = SERVICE_CONTROL_INTERROGATE;
+    _service_status.dwWin32ExitCode    = 0;
+    _service_status.dwCheckPoint       = 0;
+    _service_status.dwWaitHint         = 0;
+    _service_status.dwServiceSpecificExitCode = 0;
+
+    apxLogWrite(APXLOG_MARK_DEBUG "Inside ServiceMain...");
+
+    if (IS_VALID_STRING(_service_name)) {
+        WCHAR en[SIZ_HUGLEN];
+        int i;
+        PSECURITY_ATTRIBUTES sa = GetNullACL();
+        lstrlcpyW(en, SIZ_DESLEN, L"Global\\");
+        lstrlcatW(en, SIZ_DESLEN, _service_name);
+        lstrlcatW(en, SIZ_DESLEN, PRSRV_SIGNAL);
+        for (i = 7; i < lstrlenW(en); i++) {
+            if (en[i] == L' ')
+                en[i] = L'_';
+            else
+                en[i] = towupper(en[i]);
+        }
+        gSignalEvent = CreateEventW(sa, TRUE, FALSE, en);
+        CleanNullACL((void *)sa);
+
+        if (gSignalEvent) {
+            DWORD tid;
+            gSignalThread = CreateThread(NULL, 0, eventThread, NULL, 0, &tid);
+        }
+    }
+    /* Check the StartMode */
+    if (IS_VALID_STRING(SO_STARTMODE)) {
+        if (!lstrcmpiW(SO_STARTMODE, PRSRV_JVM)) {
+            _jni_startup = TRUE;
+            if (IS_VALID_STRING(SO_STARTCLASS)) {
+                _jni_rclass  = WideToANSI(SO_STARTCLASS);
+                /* Exchange all dots with slashes */
+                apxStrCharReplaceA(_jni_rclass, '.', '/');
+            }
+            else {
+                /* Presume its main */
+                _jni_rclass = WideToANSI(L"Main");
+            }
+            _jni_rparam = SO_STARTPARAMS;
+        }
+        else if (!lstrcmpiW(SO_STARTMODE, PRSRV_JAVA)) {
+            LPWSTR jx = NULL, szJH = SO_JAVAHOME;
+            if (!szJH)
+                szJH = apxGetJavaSoftHome(gPool, FALSE);
+            else if (!lstrcmpiW(szJH, PRSRV_JDK)) {
+                /* Figure out the JDK JavaHome */
+                szJH = apxGetJavaSoftHome(gPool, FALSE);
+            }
+            else if (!lstrcmpiW(szJH, PRSRV_JRE)) {
+                /* Figure out the JRE JavaHome */
+                szJH = apxGetJavaSoftHome(gPool, TRUE);
+            }
+            if (szJH) {
+                jx = apxPoolAlloc(gPool, (lstrlenW(szJH) + 16) * sizeof(WCHAR));
+                lstrcpyW(jx, szJH);
+                lstrcatW(jx, PRSRV_JBIN);
+                if (!SO_STARTPATH) {
+                    /* Use JAVA_HOME/bin as start path */
+                    LPWSTR szJP = apxPoolAlloc(gPool, (lstrlenW(szJH) + 8) * sizeof(WCHAR));
+                    lstrcpyW(szJP, szJH);
+                    lstrcatW(szJP, PRSRV_PBIN);
+                    SO_STARTPATH = szJP;
+                }
+            }
+            else {
+                apxLogWrite(APXLOG_MARK_ERROR "Unable to find Java Runtime Environment.");
+                goto cleanup;
+            }
+            _java_startup = TRUE;
+            /* StartImage now contains the full path to the java.exe */
+            SO_STARTIMAGE = jx;
+        }
+    }
+    /* Check the StopMode */
+    if (IS_VALID_STRING(SO_STOPMODE)) {
+        if (!lstrcmpiW(SO_STOPMODE, PRSRV_JVM)) {
+            _jni_shutdown = TRUE;
+            if (IS_VALID_STRING(SO_STOPCLASS)) {
+                _jni_sclass = WideToANSI(SO_STOPCLASS);
+                apxStrCharReplaceA(_jni_sclass, '.', '/');
+            }
+            else {
+                /* Defaults to Main */
+                _jni_sclass = WideToANSI(L"Main");
+            }
+            _jni_sparam = SO_STOPPARAMS;
+        }
+        else if (!lstrcmpiW(SO_STOPMODE, PRSRV_JAVA)) {
+            LPWSTR jx = NULL, szJH = SO_JAVAHOME;
+            if (!szJH)
+                szJH = apxGetJavaSoftHome(gPool, FALSE);
+            else if (!lstrcmpiW(szJH, PRSRV_JDK)) {
+                /* Figure out the JDK JavaHome */
+                szJH = apxGetJavaSoftHome(gPool, FALSE);
+            }
+            else if (!lstrcmpiW(szJH, PRSRV_JRE)) {
+                /* Figure out the JRE JavaHome */
+                szJH = apxGetJavaSoftHome(gPool, TRUE);
+            }
+            if (szJH) {
+                jx = apxPoolAlloc(gPool, (lstrlenW(szJH) + 16) * sizeof(WCHAR));
+                lstrcpyW(jx, szJH);
+                lstrcatW(jx, PRSRV_JBIN);
+                if (!SO_STOPPATH) {
+                    LPWSTR szJP = apxPoolAlloc(gPool, (lstrlenW(szJH) + 8) * sizeof(WCHAR));
+                    lstrcpyW(szJP, szJH);
+                    lstrcatW(szJP, PRSRV_PBIN);
+                    /* Use JAVA_HOME/bin as stop path */
+                    SO_STOPPATH = szJP;
+                }
+            }
+            else {
+                apxLogWrite(APXLOG_MARK_ERROR "Unable to find Java Runtime Environment.");
+                goto cleanup;
+            }
+            _java_shutdown = TRUE;
+            /* StopImage now contains the full path to the java.exe */
+            SO_STOPIMAGE = jx;
+        }
+    }
+    /* Find the classpath */
+    if (_jni_shutdown || _jni_startup) {
+        if (IS_VALID_STRING(SO_JVM)) {
+            if (lstrcmpW(SO_JVM, PRSRV_AUTO))
+                _jni_jvmpath = SO_JVM;
+        }
+        if (IS_VALID_STRING(SO_CLASSPATH))
+            _jni_classpath = WideToANSI(SO_CLASSPATH);
+        if (IS_VALID_STRING(SO_STARTMETHOD))
+            _jni_rmethod   = WideToANSI(SO_STARTMETHOD);
+        if (IS_VALID_STRING(SO_STOPMETHOD))
+            _jni_smethod   = WideToANSI(SO_STOPMETHOD);
+        _jni_jvmoptions    = MzWideToANSI(SO_JVMOPTIONS);
+        _jni_jvmoptions9   = MzWideToANSI(SO_JVMOPTIONS9);
+    }
+    if (_service_mode) {
+        /* Register Service Control handler */
+        _service_status_handle = RegisterServiceCtrlHandlerW(_service_name,
+                                                              service_ctrl_handler);
+        if (IS_INVALID_HANDLE(_service_status_handle)) {
+            apxLogWrite(APXLOG_MARK_ERROR "Failed to register Service Control for %S",
+                        _service_name);
+            goto cleanup;
+        }
+        /* Allocate console so that events gets processed */
+        if (!AttachConsole(ATTACH_PARENT_PROCESS) &&
+             GetLastError() == ERROR_INVALID_HANDLE) {
+            HWND hc;
+            AllocConsole();
+            if ((hc = GetConsoleWindow()) != NULL)
+                ShowWindow(hc, SW_HIDE);
+        }
+    }
+    reportServiceStatus(SERVICE_START_PENDING, NO_ERROR, 3000);
+    if ((rc = serviceStart()) == 0) {
+        /* Service is started */
+        reportServiceStatus(SERVICE_RUNNING, NO_ERROR, 0);
+        apxLogWrite(APXLOG_MARK_DEBUG "Waiting for worker to finish...");
+        /* Set console handler to capture CTRL events */
+        SetConsoleCtrlHandler((PHANDLER_ROUTINE)console_handler, TRUE);
+
+        apxHandleWait(gWorker, INFINITE, FALSE);
+        apxLogWrite(APXLOG_MARK_DEBUG "Worker finished.");
+    }
+    else {
+        apxLogWrite(APXLOG_MARK_ERROR "ServiceStart returned %d", rc);
+        goto cleanup;
+    }
+    if (gShutdownEvent) {
+
+        /* Ensure that shutdown thread exits before us */
+        apxLogWrite(APXLOG_MARK_DEBUG "Waiting for ShutdownEvent");
+        reportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, ONE_MINUTE);
+        WaitForSingleObject(gShutdownEvent, ONE_MINUTE);
+        apxLogWrite(APXLOG_MARK_DEBUG "ShutdownEvent signaled");
+        CloseHandle(gShutdownEvent);
+        gShutdownEvent = NULL;
+
+        /* This will cause to wait for all threads to exit
+         */
+        apxLogWrite(APXLOG_MARK_DEBUG "Waiting 1 minute for all threads to exit");
+        reportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, ONE_MINUTE);
+        apxDestroyJvm(ONE_MINUTE);
+    }
+    else {
+        /* We came here without shutdown event
+         * Probably because main() returned without ensuring all threads
+         * have finished
+         */
+        apxLogWrite(APXLOG_MARK_DEBUG "Waiting for all threads to exit");
+        apxDestroyJvm(INFINITE);
+        reportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
+    }
+    apxLogWrite(APXLOG_MARK_DEBUG "JVM destroyed.");
+    reportServiceStatusStopped(apxGetVmExitCode());
+
+    return;
+cleanup:
+    /* Cleanup */
+    reportServiceStatusStopped(rc);
+    gExitval = rc;
+    return;
+}
+
+
+/* Run the service in the debug mode */
+BOOL docmdDebugService(LPAPXCMDLINE lpCmdline)
+{
+    _service_mode = FALSE;
+    _service_name = lpCmdline->szApplication;
+    apxLogWrite(APXLOG_MARK_INFO "Debugging '%S' service...", _service_name);
+    serviceMain(0, NULL);
+    apxLogWrite(APXLOG_MARK_INFO "Debug service finished with exit code %d", gExitval);
+    SAFE_CLOSE_HANDLE(gPidfileHandle);
+    if (gPidfileName) {
+   	    DeleteFileW(gPidfileName);
+    }
+    return gExitval == 0 ? TRUE : FALSE;
+}
+
+BOOL docmdRunService(LPAPXCMDLINE lpCmdline)
+{
+    BOOL rv;
+    SERVICE_TABLE_ENTRYW dispatch_table[] = {
+        { lpCmdline->szApplication, (LPSERVICE_MAIN_FUNCTIONW)serviceMain },
+        { NULL, NULL }
+    };
+    _service_mode = TRUE;
+    _service_name = lpCmdline->szApplication;
+    apxLogWrite(APXLOG_MARK_INFO "Running '%S' Service...", _service_name);
+    if (StartServiceCtrlDispatcherW(dispatch_table)) {
+        apxLogWrite(APXLOG_MARK_INFO "Run service finished.");
+        rv = TRUE;
+    }
+    else {
+        apxLogWrite(APXLOG_MARK_ERROR "StartServiceCtrlDispatcher for '%S' failed",
+                    lpCmdline->szApplication);
+        rv = FALSE;
+    }
+    SAFE_CLOSE_HANDLE(gPidfileHandle);
+    if (gPidfileName) {
+   	    DeleteFileW(gPidfileName);
+    }
+    return rv;
+}
+
+static const char *gSzProc[] = {
+    "",
+    "parse command line arguments",
+    "load configuration",
+    "run service as console application",
+    "run service",
+    "start service",
+    "stop service",
+    "update service parameters",
+    "install service",
+    "delete service",
+    NULL
+};
+
+void __cdecl main(int argc, char **argv)
+{
+    UINT rv = 0;
+
+    LPAPXCMDLINE lpCmdline;
+
+    if (argc > 1) {
+        DWORD ss = 0;
+        if (strncmp(argv[1], "//PP", 4) == 0) {
+            /* Handy sleep routine defaulting to 1 minute */
+            if (argv[1][4] && argv[1][5] && argv[1][6]) {
+                int us = atoi(argv[1] + 6);
+                if (us > 0)
+                    ss = (DWORD)us;
+            }
+            Sleep(ss * 1000);
+            ExitProcess(0);
+            return;
+        }
+        else if (strcmp(argv[1], "pause") == 0) {
+            /* Handy sleep routine defaulting to 1 minute */
+            if (argc > 2) {
+                int us = atoi(argv[2]);
+                if (us > 0)
+                    ss = (DWORD)us;
+            }
+        }
+        if (ss) {
+            Sleep(ss * 1000);
+            ExitProcess(0);
+            return;
+        }
+    }
+    apxHandleManagerInitialize();
+    /* Create the main Pool */
+    gPool = apxPoolCreate(NULL, 0);
+
+    /* Parse the command line */
+    if ((lpCmdline = apxCmdlineParse(gPool, _options, _commands, _altcmds)) == NULL) {
+        apxLogWrite(APXLOG_MARK_ERROR "Invalid command line arguments");
+        rv = 1;
+        goto cleanup;
+    }
+    apxCmdlineLoadEnvVars(lpCmdline);
+    if (lpCmdline->dwCmdIndex < 6) {
+        if (!loadConfiguration(lpCmdline) &&
+            lpCmdline->dwCmdIndex < 5) {
+            apxLogWrite(APXLOG_MARK_ERROR "Load configuration failed");
+            rv = 2;
+            goto cleanup;
+        }
+    }
+
+    apxLogOpen(gPool, SO_LOGPATH, SO_LOGPREFIX, SO_LOGROTATE);
+    apxLogLevelSetW(NULL, SO_LOGLEVEL);
+    apxLogWrite(APXLOG_MARK_DEBUG "Apache Commons Daemon procrun log initialized");
+    if (SO_LOGROTATE)
+        apxLogWrite(APXLOG_MARK_DEBUG "Log will rotate each %d seconds.", SO_LOGROTATE);
+
+    apxLogWrite(APXLOG_MARK_INFO "Apache Commons Daemon procrun (%s %d-bit) started",
+                PRG_VERSION, PRG_BITS);
+
+    AplZeroMemory(&gStdwrap, sizeof(APX_STDWRAP));
+    gStartPath = lpCmdline->szExePath;
+    gStdwrap.szLogPath = SO_LOGPATH;
+    /* In debug mode allways use console */
+    if (lpCmdline->dwCmdIndex != 1) {
+        gStdwrap.szStdOutFilename = SO_STDOUTPUT;
+        gStdwrap.szStdErrFilename = SO_STDERROR;
+    }
+    redirectStdStreams(&gStdwrap, lpCmdline);
+    if (lpCmdline->dwCmdIndex == 2) {
+        SYSTEMTIME t;
+        GetLocalTime(&t);
+        fprintf(stdout, "\n%d-%02d-%02d %02d:%02d:%02d "
+                        "Apache Commons Daemon procrun stdout initialized\n",
+                        t.wYear, t.wMonth, t.wDay,
+                        t.wHour, t.wMinute, t.wSecond);
+        fprintf(stderr, "\n%d-%02d-%02d %02d:%02d:%02d "
+                        "Apache Commons Daemon procrun stderr initialized\n",
+                        t.wYear, t.wMonth, t.wDay,
+                        t.wHour, t.wMinute, t.wSecond);
+    }
+    switch (lpCmdline->dwCmdIndex) {
+        case 1: /* Run Service as console application */
+            if (!docmdDebugService(lpCmdline))
+                rv = 3;
+        break;
+        case 2: /* Run Service */
+            if (!docmdRunService(lpCmdline))
+                rv = 4;
+        break;
+        case 3: /* Start service */
+            if (!docmdStartService(lpCmdline))
+                rv = 5;
+        break;
+        case 4: /* Stop Service */
+            if (!docmdStopService(lpCmdline))
+                rv = 6;
+        break;
+        case 5: /* Update Service parameters */
+            if (!docmdUpdateService(lpCmdline))
+                rv = 7;
+        break;
+        case 6: /* Install Service */
+            if (!docmdInstallService(lpCmdline))
+                rv = 8;
+        break;
+        case 7: /* Delete Service */
+            if (!docmdDeleteService(lpCmdline))
+                rv = 9;
+        break;
+        case 8: /* Print Usage and exit */
+            printUsage(lpCmdline, TRUE);
+        break;
+        case 9: /* Print version and exit */
+            printVersion();
+        break;
+        default:
+            /* Unknown command option */
+            apxLogWrite(APXLOG_MARK_ERROR "Unknown command line option");
+            printUsage(lpCmdline, FALSE);
+            rv = 99;
+        break;
+    }
+
+cleanup:
+    if (rv) {
+        int ix = 0;
+        if (rv > 0 && rv < 10)
+            ix = rv;
+        apxLogWrite(APXLOG_MARK_ERROR "Apache Commons Daemon procrun failed "
+                                      "with exit value: %d (Failed to %s)",
+                                      rv, gSzProc[ix]);
+        if (ix > 2 && !_service_mode) {
+            /* Print something to the user console */
+            apxDisplayError(FALSE, NULL, 0, "Failed to %s", gSzProc[ix]);
+        }
+    }
+    else
+        apxLogWrite(APXLOG_MARK_INFO "Apache Commons Daemon procrun finished");
+    if (lpCmdline)
+        apxCmdlineFree(lpCmdline);
+    _service_status_handle = NULL;
+    _service_mode = FALSE;
+    _flushall();
+    apxLogClose(NULL);
+    apxHandleManagerDestroy();
+    ExitProcess(rv);
+}

Added: tomee/deps/branches/commons-daemon/src/native/windows/apps/prunsrv/prunsrv.h
URL: http://svn.apache.org/viewvc/tomee/deps/branches/commons-daemon/src/native/windows/apps/prunsrv/prunsrv.h?rev=1860225&view=auto
==============================================================================
--- tomee/deps/branches/commons-daemon/src/native/windows/apps/prunsrv/prunsrv.h (added)
+++ tomee/deps/branches/commons-daemon/src/native/windows/apps/prunsrv/prunsrv.h Tue May 28 09:46:53 2019
@@ -0,0 +1,32 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* ====================================================================
+ * jar2exe -- convert .jar file to WIN32 executable.
+ * Contributed by Mladen Turk <mt...@apache.org>
+ * 05 Aug 2003
+ * ====================================================================
+ */
+
+#ifndef _PRUNSRV_H
+#define _PRUNSRV_H
+
+#undef  PRG_VERSION
+#define PRG_VERSION    "1.1.1.0"
+#define PRG_REGROOT   L"Apache Software Foundation\\Procrun 2.0"
+
+#endif /* _PRUNSRV_H */
+

Added: tomee/deps/branches/commons-daemon/src/native/windows/apps/prunsrv/prunsrv.manifest
URL: http://svn.apache.org/viewvc/tomee/deps/branches/commons-daemon/src/native/windows/apps/prunsrv/prunsrv.manifest?rev=1860225&view=auto
==============================================================================
--- tomee/deps/branches/commons-daemon/src/native/windows/apps/prunsrv/prunsrv.manifest (added)
+++ tomee/deps/branches/commons-daemon/src/native/windows/apps/prunsrv/prunsrv.manifest Tue May 28 09:46:53 2019
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+<assemblyIdentity version="3.1.0.0" processorArchitecture="*" name="Apache.Procrun.Prunsrv" type="win32" />
+<description>Apache Commons Daemon Service Runner</description>
+<dependency>
+<dependentAssembly>
+<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*" />
+</dependentAssembly>
+</dependency>
+<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+<security>
+<!-- Windows UAC support -->
+<requestedPrivileges>
+<requestedExecutionLevel level="requireAdministrator" uiAccess="false"></requestedExecutionLevel>
+</requestedPrivileges>
+</security>
+</trustInfo>
+</assembly>

Added: tomee/deps/branches/commons-daemon/src/native/windows/apps/prunsrv/prunsrv.rc
URL: http://svn.apache.org/viewvc/tomee/deps/branches/commons-daemon/src/native/windows/apps/prunsrv/prunsrv.rc?rev=1860225&view=auto
==============================================================================
--- tomee/deps/branches/commons-daemon/src/native/windows/apps/prunsrv/prunsrv.rc (added)
+++ tomee/deps/branches/commons-daemon/src/native/windows/apps/prunsrv/prunsrv.rc Tue May 28 09:46:53 2019
@@ -0,0 +1,58 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apxwin.h"
+#include "prunsrv.h"
+
+#define RSTR_PRUNSRV "Apache Commons Daemon Service Runner"
+
+IDI_MAINICON         ICON                   "../../resources/procrunw.ico"
+CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "prunsrv.manifest"
+
+1 VERSIONINFO
+ FILEVERSION 1,1,1,0
+ PRODUCTVERSION 1,1,1,0
+ FILEFLAGSMASK 0x3fL
+#if defined(_DEBUG)
+ FILEFLAGS 0x03L
+#else
+ FILEFLAGS 0x02L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+  BLOCK "StringFileInfo"
+  BEGIN
+    BLOCK "040904b0"
+    BEGIN
+      VALUE "Comments", "\0"
+      VALUE "CompanyName", "Apache Software Foundation\0"
+      VALUE "FileDescription", RSTR_PRUNSRV "\0"
+      VALUE "FileVersion", PRG_VERSION
+      VALUE "InternalName", RSTR_PRUNSRV "\0"
+      VALUE "LegalCopyright", "Copyright (c) 2000-2017 The Apache Software Foundation.\0"
+      VALUE "OriginalFilename", "prunsrv.exe\0"
+      VALUE "ProductName", RSTR_PRUNSRV "\0"
+      VALUE "ProductVersion", PRG_VERSION
+    END
+  END
+  BLOCK "VarFileInfo"
+  BEGIN
+    VALUE "Translation", 0x409, 1200
+  END
+END
+

Added: tomee/deps/branches/commons-daemon/src/native/windows/include/Makefile.inc
URL: http://svn.apache.org/viewvc/tomee/deps/branches/commons-daemon/src/native/windows/include/Makefile.inc?rev=1860225&view=auto
==============================================================================
--- tomee/deps/branches/commons-daemon/src/native/windows/include/Makefile.inc (added)
+++ tomee/deps/branches/commons-daemon/src/native/windows/include/Makefile.inc Tue May 28 09:46:53 2019
@@ -0,0 +1,319 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+# ====================================================================
+#
+# Makefile.inc Master makefile definitions.
+#                 This file defines CPU architecture and basic compiler
+#                 and linker parameters.
+# Common params:
+#                 CPU       Compile for specified CPU. Supported CPUs are:
+#                           X86 (Common x86 architecture)
+#                           X64 (AMD64/EMT64 architecture)
+#                           If not specified it will default to the
+#                           BUILD_CPU environment variable, failing that
+#                           It will default to the
+#                           PROCESSOR_ARCHITECTURE/ARCHITEW6432 environment variables
+#                           or failing that it will default to X86.
+#                 WINVER    Compile for specified Windows version
+#                           WINXP   for Windows XP and up (default)
+#                           WIN2003 for Windows 2003 and up
+#                           VISTA   for Windows Vista and up
+#                           WIN7    for Windows 7 and up
+#                           Deprecated targets (may not even compile):
+#                               WINNT   for Windows 2000 and up
+#                               NT4     for Windows NT4 and up
+#                               WIN9X   for Windows 95, 98 and Me
+#                 BUILD     Build version
+#                           RETAIL or RELEASE (default)
+#                           DEBUG
+#                 TARGET    Build application target
+#                           EXE Console executable (default)
+#                           GUI Windows GUI executable
+#                           DLL Dynamic Link Library
+#                           LIB Static library
+# Environment variables  used:
+#           EXTRA_CFLAGS    Added to the common CFLAGS
+#           EXTRA_CXXFLAGS  Added to the common CXXFLAGS
+#           EXTRA_LIBS      Added to the common LIBS
+#           EXTRA_LFLAGS    Added to the common LFLAGS
+#           EXTRA_RCFLAGS   Added to the common RCFLAGS
+#
+# Compiler tools environment variables:
+#                 CC        C compiler  (defaults to cl.exe)
+#                 CXX       C++ compiler  (defaults to $CC -TP)
+#                 CPP       C preprocessor  (defaults to $CC -EP)
+#                 LINK      Linker (defaults to link.exe)
+#                 RC        Resource compiler (defaults to rc.exe)
+#                 MT        Manifest toolkit (defaults to mt.exe)
+#                 ML        Assembler (defaults to ml.exe or ml64.exe)
+#
+# Originally contributed by Mladen Turk <mturk jboss.com>
+#
+# ====================================================================
+#
+
+# C/C++ compiler
+!IF !DEFINED(CC) || "$(CC)" == ""
+CC = cl.exe
+!ENDIF
+!IF !DEFINED(CXX) || "$(CXX)" == ""
+CXX = $(CC) -TP
+!ENDIF
+!IF !DEFINED(CPP) || "$(CPP)" == ""
+CPP = $(CC) -EP
+!ENDIF
+# Linker
+!IF !DEFINED(LINK) || "$(LINK)" == ""
+LINK = link.exe
+!ENDIF
+# Resource Compiler
+!IF !DEFINED(RC) || "$(RC)" == ""
+RC = rc.exe
+!ENDIF
+# Manifest toolkit
+!IF !DEFINED(MT) || "$(MT)" == ""
+MT = mt.exe
+!ENDIF
+
+# If CPU is not defined, apply defaults
+!IF !DEFINED(CPU) || "$(CPU)" == ""
+# Set BUILD_CPU if it is not yet set
+!IF !DEFINED(BUILD_CPU) || "$(BUILD_CPU)" == ""
+!IF "$(PROCESSOR_ARCHITECTURE)" == ""
+!IF "$(PROCESSOR_ARCHITEW6432)" == ""
+# Default to x86, will be upcased later
+BUILD_CPU=x86
+!ELSE
+BUILD_CPU=$(PROCESSOR_ARCHITEW6432)
+!ENDIF
+!ELSE
+BUILD_CPU=$(PROCESSOR_ARCHITECTURE)
+!ENDIF
+!ENDIF
+# Check BUILD_CPU and reset value if necessary
+!IF "$(BUILD_CPU)" == "i386" || "$(BUILD_CPU)" == "x86" || "$(BUILD_CPU)" == "i686"
+CPU=X86
+!ENDIF
+!IF "$(BUILD_CPU)" == "amd64" || "$(BUILD_CPU)" == "x86_64" || "$(BUILD_CPU)" == "x64"
+CPU=X64
+!ENDIF
+# did we manage to set CPU?
+!IF !DEFINED(CPU) || "$(CPU)" == ""
+!ERROR Unexpected value of BUILD_CPU: $(BUILD_CPU) or PROCESSOR_ARCHITECTURE=$(PROCESSOR_ARCHITECTURE) or PROCESSOR_ARCHITEW6432=$(PROCESSOR_ARCHITEW6432).
+!ENDIF
+!ENDIF
+
+!IF "$(CPU)" != "X86"
+!IF "$(CPU)" != "X64"
+!ERROR Must specify CPU environment variable (X86, X64) $(CPU)
+!ENDIF
+!ENDIF
+
+!IF !DEFINED(TARGET) || "$(TARGET)" == ""
+TARGET=EXE
+!ENDIF
+
+!IF "$(TARGET)" != "EXE"
+!IF "$(TARGET)" != "GUI"
+!IF "$(TARGET)" != "DLL"
+!IF "$(TARGET)" != "LIB"
+!ERROR Must specify TARGET environment variable (EXE, GUI, DLL, LIB)
+!ENDIF
+!ENDIF
+!ENDIF
+!ENDIF
+
+!IF !DEFINED(WINVER) || "$(WINVER)" == ""
+WINVER=WINXP
+!ENDIF
+
+
+!IF "$(WINVER)" == "WINXP"
+NMAKE_WINVER = 0x0501
+_WIN32_IE = 0x0600
+!ELSEIF "$(WINVER)" == "WIN2003"
+NMAKE_WINVER = 0x0502
+_WIN32_IE = 0x0600
+!ELSEIF "$(WINVER)" == "VISTA"
+NMAKE_WINVER = 0x0600
+_WIN32_IE = 0x0700
+!ELSEIF "$(WINVER)" == "WIN7"
+NMAKE_WINVER = 0x0700
+_WIN32_IE = 0x0800
+!ELSE
+!ERROR Must specify WINVER environment variable (WINXP, WIN2003, VISTA, WIN7)
+!ENDIF
+
+NMAKE_WINNT = -D_WINNT -D_WIN32_WINNT=$(NMAKE_WINVER) -DWINVER=$(NMAKE_WINVER) -D_WIN32_IE=$(_WIN32_IE)
+
+!IF !DEFINED(BUILD) || "$(BUILD)" == ""
+BUILD=RELEASE
+!ENDIF
+!IFDEF RELEASE
+BUILD=RELEASE
+!ENDIF
+!IFDEF DEBUG
+BUILD=DEBUG
+!ENDIF
+!IFDEF NODEBUG
+BUILD=RELEASE
+!ENDIF
+
+!IF "$(BUILD)" != "RELEASE"
+!IF "$(BUILD)" != "DEBUG"
+!ERROR Must specify BUILD environment variable (RELEASE, DEBUG)
+!ENDIF
+!ENDIF
+
+# Common flags for all platforms
+CMN_CFLAGS = -c -nologo -DWIN32 -D_WIN32 -D_WINDOWS $(NMAKE_WINNT) -W3
+!IF "$(TARGET)" == "EXE"
+CMN_CFLAGS = $(CMN_CFLAGS) -D_CONSOLE
+!ENDIF
+# Mark that extern C newer throws C++ exception
+CMN_CFLAGS = $(CMN_CFLAGS) -EHsc
+
+# All supported platforms support unicode
+CMN_CFLAGS = $(CMN_CFLAGS) -D_UNICODE -DUNICODE
+
+!IF "$(CPU)" == "X86"
+CPU_CFLAGS = -D_X86_=1
+MACHINE=X86
+MACHINE_LC=i386
+!ELSEIF "$(CPU)" == "X64"
+CPU_CFLAGS = -D_AMD64_=1 -DWIN64 -D_WIN64
+MACHINE=AMD64
+MACHINE_LC=amd64
+!ENDIF
+
+!IF "$(BUILD)" == "RELEASE"
+!IF "$(CPU)" == "X86"
+OPT_CFLAGS = -O2 -Ob2 -Oy- -Zi -DNDEBUG
+!ELSE
+OPT_CFLAGS = -O2 -Ob2 -Zi -DNDEBUG
+!ENDIF
+!ELSE
+OPT_CFLAGS = -Od -Zi -DDEBUG -D_DEBUG
+!ENDIF
+
+!IF DEFINED(STATIC_CRT)
+CRT_CFLAGS = -D_MT -MT
+!ELSE
+CRT_CFLAGS = -D_MT -MD
+!ENDIF
+
+!IF "$(BUILD)" == "DEBUG"
+CRT_CFLAGS = $(CRT_CFLAGS)d
+!ENDIF
+
+CFLAGS = $(CMN_CFLAGS) $(CPU_CFLAGS) $(OPT_CFLAGS) $(CRT_CFLAGS)
+
+!IF DEFINED(EXTRA_CFLAGS)
+CFLAGS = $(CFLAGS) $(EXTRA_CFLAGS)
+!ENDIF
+
+# Cleanup CXXFLAGS
+CXXFLAGS =
+!IF DEFINED(EXTRA_CXXFLAGS)
+CXXFLAGS = $(EXTRA_CXXFLAGS)
+!ENDIF
+
+# Linker section
+LIBS = kernel32.lib advapi32.lib ws2_32.lib mswsock.lib ole32.lib shell32.lib rpcrt4.lib
+# Extra libs from command line or env
+!IF DEFINED(EXTRA_LIBS)
+LIBS = $(LIBS) $(EXTRA_LIBS)
+!ENDIF
+
+# Run-Time Error Checks
+!IF "$(BUILD)" == "DEBUG"
+!IF DEFINED(RTC_CHECK)
+LIBS = $(LIBS) RunTmChk.lib
+CFLAGS = $(CFLAGS) -RTC1
+!ENDIF
+!ENDIF
+
+COMMON_LFLAGS = /NOLOGO
+
+# Always add debugging to the linker
+OPT_LFLAGS = /INCREMENTAL:NO /DEBUG
+!IF "$(BUILD)" == "RELEASE"
+OPT_LFLAGS = $(OPT_LFLAGS) /OPT:REF
+!ENDIF
+
+!IF "$(TARGET)" == "EXE"
+LFLAGS = $(COMMON_LFLAGS) /SUBSYSTEM:CONSOLE /MACHINE:$(MACHINE)
+!ELSEIF "$(TARGET)" == "GUI"
+LFLAGS = $(COMMON_LFLAGS) /SUBSYSTEM:WINDOWS /MACHINE:$(MACHINE)
+!ELSEIF "$(TARGET)" == "DLL"
+LFLAGS = $(COMMON_LFLAGS) /DLL /SUBSYSTEM:WINDOWS /MACHINE:$(MACHINE)
+!ELSEIF "$(TARGET)" == "LIB"
+LFLAGS = -lib $(COMMON_LFLAGS)
+!ENDIF
+
+!IF DEFINED(EXTRA_LFLAGS)
+LFLAGS = $(LFLAGS) $(EXTRA_LFLAGS)
+!ENDIF
+
+!IF "$(TARGET)" != "LIB"
+LFLAGS = $(LFLAGS) $(OPT_LFLAGS)
+!ENDIF
+
+# Resource compiler flags
+
+RCFLAGS=/l 0x409
+!IF "$(BUILD)" == "RELEASE"
+RCFLAGS = $(RCFLAGS) /d "NDEBUG"
+!ELSE
+RCFLAGS = $(RCFLAGS) /d "_DEBUG" /d "DEBUG"
+!ENDIF
+RCFLAGS = $(RCFLAGS)
+!IF DEFINED(EXTRA_RCFLAGS)
+RCFLAGS = $(RCFLAGS) $(EXTRA_RCFLAGS)
+!ENDIF
+
+
+# Build Target dir e.g. WINNT_I386_RELEASE_DLL
+!IF !DEFINED(WORKDIR) || "$(WORKDIR)" == ""
+!IF !DEFINED(WORKDIR_EXT) || "$(WORKDIR_EXT)" == ""
+WORKDIR = $(WINVER)_$(CPU)_$(TARGET)_$(BUILD)
+WORKDIR_DLL = $(WINVER)_$(CPU)_DLL_$(BUILD)
+WORKDIR_LIB = $(WINVER)_$(CPU)_LIB_$(BUILD)
+WORKDIR_EXE = $(WINVER)_$(CPU)_EXE_$(BUILD)
+!ELSE
+WORKDIR = $(WINVER)_$(CPU)_$(BUILDIR_EXT)_$(BUILD)
+!ENDIF
+!ENDIF
+
+CLEANTARGET=if exist "$(WORKDIR)\$(NULL)" rd /s /q $(WORKDIR)
+MAKEWORKDIR=if not exist "$(WORKDIR)\$(NULL)" mkdir $(WORKDIR)
+MAKEINSTALL=if not exist "$(INSTALLLOC)\$(NULL)" mkdir $(INSTALLLOC)
+
+!IF DEFINED(JAVA_HOME) && "$(JAVA_HOME)" != ""
+JAVA_INCLUDES=-I "$(JAVA_HOME)\include" -I "$(JAVA_HOME)\include\win32"
+!ENDIF
+
+# Assembler Section
+!IF !DEFINED(ML) || "$(ML)" == ""
+!IF "$(CPU)" == "X86"
+ML = ml.exe
+AFLAGS = /coff /Zi /c
+!ELSEIF "$(CPU)" == "X64"
+ML = ml64.exe
+AFLAGS = /Zi /c
+!ENDIF
+!ENDIF