Skip to content
This repository
Browse code

initial import

git-svn-id: https://svn.kenai.com/svn/winsw~subversion/trunk@2 c8b2a3fe-9b5b-6a51-a37e-dc31b0e308fa
  • Loading branch information...
commit e94628badf55ef4dae431abb0b8c5692e495ebe7 1 parent 08eed56
authored September 26, 2008
348  DynamicProxy.cs
... ...
@@ -0,0 +1,348 @@
  1
+using System;
  2
+using System.Reflection;
  3
+using System.Collections;
  4
+using System.Reflection.Emit;
  5
+using System.Threading;
  6
+
  7
+namespace DynamicProxy
  8
+{
  9
+    /// <summary>
  10
+    /// Interface that a user defined proxy handler needs to implement.  This interface 
  11
+    /// defines one method that gets invoked by the generated proxy.  
  12
+    /// </summary>
  13
+    public interface IProxyInvocationHandler
  14
+    {
  15
+        /// <param name="proxy">The instance of the proxy</param>
  16
+        /// <param name="method">The method info that can be used to invoke the actual method on the object implementation</param>
  17
+        /// <param name="parameters">Parameters to pass to the method</param>
  18
+        /// <returns>Object</returns>
  19
+        object Invoke(object proxy, MethodInfo method, object[] parameters);
  20
+    }
  21
+
  22
+    /// <summary>
  23
+    /// Factory class used to cache Types instances
  24
+    /// </summary>
  25
+    public class MetaDataFactory
  26
+    {
  27
+        private static Hashtable typeMap = new Hashtable();
  28
+
  29
+        /// <summary>
  30
+        /// Class constructor.  Private because this is a static class.
  31
+        /// </summary>
  32
+        private MetaDataFactory()
  33
+        {
  34
+        }
  35
+
  36
+        ///<summary>
  37
+        /// Method to add a new Type to the cache, using the type's fully qualified
  38
+        /// name as the key
  39
+        ///</summary>
  40
+        ///<param name="interfaceType">Type to cache</param>
  41
+        public static void Add(Type interfaceType)
  42
+        {
  43
+            if (interfaceType != null)
  44
+            {
  45
+                lock (typeMap.SyncRoot)
  46
+                {
  47
+                    if (!typeMap.ContainsKey(interfaceType.FullName))
  48
+                    {
  49
+                        typeMap.Add(interfaceType.FullName, interfaceType);
  50
+                    }
  51
+                }
  52
+            }
  53
+        }
  54
+
  55
+        ///<summary>
  56
+        /// Method to return the method of a given type at a specified index.
  57
+        ///</summary>
  58
+        ///<param name="name">Fully qualified name of the method to return</param>
  59
+        ///<param name="i">Index to use to return MethodInfo</param>
  60
+        ///<returns>MethodInfo</returns>
  61
+        public static MethodInfo GetMethod(string name, int i)
  62
+        {
  63
+            Type type = null;
  64
+            lock (typeMap.SyncRoot)
  65
+            {
  66
+                type = (Type)typeMap[name];
  67
+            }
  68
+
  69
+            return type.GetMethods()[i];
  70
+        }
  71
+
  72
+        public static PropertyInfo GetProperty(string name, int i)
  73
+        {
  74
+            Type type = null;
  75
+            lock (typeMap.SyncRoot)
  76
+            {
  77
+                type = (Type)typeMap[name];
  78
+            }
  79
+
  80
+            return type.GetProperties()[i];
  81
+        }
  82
+    }
  83
+
  84
+    /// <summary>
  85
+    /// </summary>
  86
+    public class ProxyFactory
  87
+    {
  88
+        private static ProxyFactory instance;
  89
+        private static Object lockObj = new Object();
  90
+
  91
+        private Hashtable typeMap = Hashtable.Synchronized(new Hashtable());
  92
+        private static readonly Hashtable opCodeTypeMapper = new Hashtable();
  93
+
  94
+        private const string PROXY_SUFFIX = "Proxy";
  95
+        private const string ASSEMBLY_NAME = "ProxyAssembly";
  96
+        private const string MODULE_NAME = "ProxyModule";
  97
+        private const string HANDLER_NAME = "handler";
  98
+
  99
+        // Initialize the value type mapper.  This is needed for methods with intrinsic 
  100
+        // return types, used in the Emit process.
  101
+        static ProxyFactory()
  102
+        {
  103
+            opCodeTypeMapper.Add(typeof(System.Boolean), OpCodes.Ldind_I1);
  104
+            opCodeTypeMapper.Add(typeof(System.Int16), OpCodes.Ldind_I2);
  105
+            opCodeTypeMapper.Add(typeof(System.Int32), OpCodes.Ldind_I4);
  106
+            opCodeTypeMapper.Add(typeof(System.Int64), OpCodes.Ldind_I8);
  107
+            opCodeTypeMapper.Add(typeof(System.Double), OpCodes.Ldind_R8);
  108
+            opCodeTypeMapper.Add(typeof(System.Single), OpCodes.Ldind_R4);
  109
+            opCodeTypeMapper.Add(typeof(System.UInt16), OpCodes.Ldind_U2);
  110
+            opCodeTypeMapper.Add(typeof(System.UInt32), OpCodes.Ldind_U4);
  111
+        }
  112
+
  113
+        private ProxyFactory()
  114
+        {
  115
+        }
  116
+
  117
+        public static ProxyFactory GetInstance()
  118
+        {
  119
+            if (instance == null)
  120
+            {
  121
+                CreateInstance();
  122
+            }
  123
+
  124
+            return instance;
  125
+        }
  126
+
  127
+        private static void CreateInstance()
  128
+        {
  129
+            lock (lockObj)
  130
+            {
  131
+                if (instance == null)
  132
+                {
  133
+                    instance = new ProxyFactory();
  134
+                }
  135
+            }
  136
+        }
  137
+
  138
+        public Object Create(IProxyInvocationHandler handler, Type objType, bool isObjInterface)
  139
+        {
  140
+            string typeName = objType.FullName + PROXY_SUFFIX;
  141
+            Type type = (Type)typeMap[typeName];
  142
+
  143
+            // check to see if the type was in the cache.  If the type was not cached, then
  144
+            // create a new instance of the dynamic type and add it to the cache.
  145
+            if (type == null)
  146
+            {
  147
+                if (isObjInterface)
  148
+                {
  149
+                    type = CreateType(handler, new Type[] { objType }, typeName);
  150
+                }
  151
+                else
  152
+                {
  153
+                    type = CreateType(handler, objType.GetInterfaces(), typeName);
  154
+                }
  155
+
  156
+                typeMap.Add(typeName, type);
  157
+            }
  158
+
  159
+            // return a new instance of the type.
  160
+            return Activator.CreateInstance(type, new object[] { handler });
  161
+        }
  162
+
  163
+        public Object Create(IProxyInvocationHandler handler, Type objType)
  164
+        {
  165
+            return Create(handler, objType, false);
  166
+        }
  167
+
  168
+        private Type CreateType(IProxyInvocationHandler handler, Type[] interfaces, string dynamicTypeName)
  169
+        {
  170
+            Type retVal = null;
  171
+
  172
+            if (handler != null && interfaces != null)
  173
+            {
  174
+                Type objType = typeof(System.Object);
  175
+                Type handlerType = typeof(IProxyInvocationHandler);
  176
+
  177
+                AppDomain domain = Thread.GetDomain();
  178
+                AssemblyName assemblyName = new AssemblyName();
  179
+                assemblyName.Name = ASSEMBLY_NAME;
  180
+                assemblyName.Version = new Version(1, 0, 0, 0);
  181
+
  182
+                // create a new assembly for this proxy, one that isn't presisted on the file system
  183
+                AssemblyBuilder assemblyBuilder = domain.DefineDynamicAssembly(
  184
+                    assemblyName, AssemblyBuilderAccess.Run);
  185
+                    // assemblyName, AssemblyBuilderAccess.RunAndSave,".");  // to save it to the disk
  186
+
  187
+                // create a new module for this proxy
  188
+                ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(MODULE_NAME);
  189
+
  190
+                // Set the class to be public and sealed
  191
+                TypeAttributes typeAttributes =
  192
+                    TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed;
  193
+
  194
+                // Gather up the proxy information and create a new type builder.  One that
  195
+                // inherits from Object and implements the interface passed in
  196
+                TypeBuilder typeBuilder = moduleBuilder.DefineType(
  197
+                    dynamicTypeName, typeAttributes, objType, interfaces);
  198
+
  199
+                // Define a member variable to hold the delegate
  200
+                FieldBuilder handlerField = typeBuilder.DefineField(
  201
+                    HANDLER_NAME, handlerType, FieldAttributes.Private);
  202
+
  203
+
  204
+                // build a constructor that takes the delegate object as the only argument
  205
+                //ConstructorInfo defaultObjConstructor = objType.GetConstructor( new Type[0] );
  206
+                ConstructorInfo superConstructor = objType.GetConstructor(new Type[0]);
  207
+                ConstructorBuilder delegateConstructor = typeBuilder.DefineConstructor(
  208
+                    MethodAttributes.Public, CallingConventions.Standard, new Type[] { handlerType });
  209
+
  210
+                #region( "Constructor IL Code" )
  211
+                ILGenerator constructorIL = delegateConstructor.GetILGenerator();
  212
+
  213
+                // Load "this"
  214
+                constructorIL.Emit(OpCodes.Ldarg_0);
  215
+                // Load first constructor parameter
  216
+                constructorIL.Emit(OpCodes.Ldarg_1);
  217
+                // Set the first parameter into the handler field
  218
+                constructorIL.Emit(OpCodes.Stfld, handlerField);
  219
+                // Load "this"
  220
+                constructorIL.Emit(OpCodes.Ldarg_0);
  221
+                // Call the super constructor
  222
+                constructorIL.Emit(OpCodes.Call, superConstructor);
  223
+                // Constructor return
  224
+                constructorIL.Emit(OpCodes.Ret);
  225
+                #endregion
  226
+
  227
+                // for every method that the interfaces define, build a corresponding 
  228
+                // method in the dynamic type that calls the handlers invoke method.  
  229
+                foreach (Type interfaceType in interfaces)
  230
+                {
  231
+                    GenerateMethod(interfaceType, handlerField, typeBuilder);
  232
+                }
  233
+
  234
+                retVal = typeBuilder.CreateType();
  235
+
  236
+                // assemblyBuilder.Save(dynamicTypeName + ".dll");
  237
+            }
  238
+
  239
+            return retVal;
  240
+        }
  241
+
  242
+        private static readonly MethodInfo INVOKE_METHOD = typeof(IProxyInvocationHandler).GetMethod("Invoke");
  243
+        private static readonly MethodInfo GET_METHODINFO_METHOD = typeof(MetaDataFactory).GetMethod("GetMethod", new Type[] { typeof(string), typeof(int) });
  244
+
  245
+        private void GenerateMethod( Type interfaceType, FieldBuilder handlerField, TypeBuilder typeBuilder ) {
  246
+            MetaDataFactory.Add( interfaceType );
  247
+            MethodInfo[] interfaceMethods = interfaceType.GetMethods();
  248
+            PropertyInfo[] props = interfaceType.GetProperties();
  249
+
  250
+            for ( int i = 0; i < interfaceMethods.Length; i++ ) {
  251
+                MethodInfo methodInfo = interfaceMethods[i];
  252
+
  253
+                // Get the method parameters since we need to create an array
  254
+                // of parameter types
  255
+                ParameterInfo[] methodParams = methodInfo.GetParameters();
  256
+                int numOfParams = methodParams.Length;
  257
+                Type[] methodParameters = new Type[ numOfParams ];
  258
+
  259
+                // convert the ParameterInfo objects into Type
  260
+                for ( int j = 0; j < numOfParams; j++ ) {
  261
+                    methodParameters[j] = methodParams[j].ParameterType;
  262
+                }
  263
+
  264
+                // create a new builder for the method in the interface
  265
+                MethodBuilder methodBuilder = typeBuilder.DefineMethod(
  266
+                    methodInfo.Name, 
  267
+                    /*MethodAttributes.Public | MethodAttributes.Virtual | */ methodInfo.Attributes&~MethodAttributes.Abstract,
  268
+                    CallingConventions.Standard,
  269
+                    methodInfo.ReturnType, methodParameters );                                                   
  270
+
  271
+                #region( "Handler Method IL Code" )
  272
+                ILGenerator methodIL = methodBuilder.GetILGenerator();
  273
+                        
  274
+                // load "this"
  275
+                methodIL.Emit( OpCodes.Ldarg_0 );
  276
+                // load the handler
  277
+                methodIL.Emit( OpCodes.Ldfld, handlerField );
  278
+                // load "this" since its needed for the call to invoke
  279
+                methodIL.Emit( OpCodes.Ldarg_0 );
  280
+                // load the name of the interface, used to get the MethodInfo object
  281
+                // from MetaDataFactory
  282
+                methodIL.Emit( OpCodes.Ldstr, interfaceType.FullName );
  283
+                // load the index, used to get the MethodInfo object 
  284
+                // from MetaDataFactory 
  285
+                methodIL.Emit( OpCodes.Ldc_I4, i ); 
  286
+                // invoke GetMethod in MetaDataFactory
  287
+                methodIL.Emit( OpCodes.Call, GET_METHODINFO_METHOD);
  288
+
  289
+                // load the number of parameters onto the stack
  290
+                methodIL.Emit( OpCodes.Ldc_I4, numOfParams );
  291
+                // create a new array, using the size that was just pused on the stack
  292
+                methodIL.Emit( OpCodes.Newarr, typeof(object) );
  293
+                        
  294
+                // if we have any parameters, then iterate through and set the values
  295
+                // of each element to the corresponding arguments
  296
+                for ( int j = 0; j < numOfParams; j++ ) {
  297
+                    methodIL.Emit( OpCodes.Dup );   // this copies the array
  298
+                    methodIL.Emit( OpCodes.Ldc_I4, j );
  299
+                    methodIL.Emit( OpCodes.Ldarg, j + 1 );
  300
+                    if ( methodParameters[j].IsValueType ) {
  301
+                        methodIL.Emit( OpCodes.Box, methodParameters[j] );
  302
+                    }
  303
+                    methodIL.Emit( OpCodes.Stelem_Ref );                                    
  304
+                }
  305
+
  306
+                // call the Invoke method
  307
+                methodIL.Emit( OpCodes.Callvirt, INVOKE_METHOD );
  308
+                        
  309
+                if ( methodInfo.ReturnType != typeof(void) ) { 
  310
+                    // if the return type if a value type, then unbox the return value
  311
+                    // so that we don't get junk.
  312
+                    if ( methodInfo.ReturnType.IsValueType  ) {
  313
+                        methodIL.Emit( OpCodes.Unbox, methodInfo.ReturnType );
  314
+                        if ( methodInfo.ReturnType.IsEnum ) {
  315
+                            methodIL.Emit( OpCodes.Ldind_I4 );
  316
+                        } else if ( !methodInfo.ReturnType.IsPrimitive ) {
  317
+                            methodIL.Emit( OpCodes.Ldobj, methodInfo.ReturnType );
  318
+                        } else {
  319
+                            methodIL.Emit( (OpCode) opCodeTypeMapper[ methodInfo.ReturnType ] );
  320
+                        }
  321
+                    }                                                                     
  322
+                } else {
  323
+                    // pop the return value that Invoke returned from the stack since
  324
+                    // the method's return type is void. 
  325
+                    methodIL.Emit( OpCodes.Pop );
  326
+                }
  327
+                                            
  328
+                // Return
  329
+                methodIL.Emit( OpCodes.Ret );
  330
+                #endregion
  331
+            }
  332
+
  333
+            //for (int i = 0; i < props.Length; i++)
  334
+            //{
  335
+            //    PropertyInfo p = props[i];
  336
+
  337
+            //    PropertyBuilder pb = typeBuilder.DefineProperty(p.Name, p.Attributes, p.PropertyType, new Type[] { p.PropertyType });
  338
+            //    pb.SetGetMethod((MethodBuilder)methodTable[p.GetGetMethod()]);
  339
+            //    pb.SetSetMethod((MethodBuilder)methodTable[p.GetSetMethod()]);
  340
+            //}
  341
+
  342
+            // Iterate through the parent interfaces and recursively call this method
  343
+            foreach ( Type parentType in interfaceType.GetInterfaces() ) {
  344
+                GenerateMethod( parentType, handlerField, typeBuilder );            
  345
+            }
  346
+        }
  347
+    }
  348
+}
335  Main.cs
... ...
@@ -0,0 +1,335 @@
  1
+using System;
  2
+using System.Collections.Generic;
  3
+using System.ComponentModel;
  4
+using System.Data;
  5
+using System.Diagnostics;
  6
+using System.ServiceProcess;
  7
+using System.Text;
  8
+using System.IO;
  9
+using WMI;
  10
+using System.Xml;
  11
+using System.Threading;
  12
+using Microsoft.Win32;
  13
+
  14
+namespace winsw
  15
+{
  16
+    /// <summary>
  17
+    /// In-memory representation of the configuration file.
  18
+    /// </summary>
  19
+    public class ServiceDescriptor
  20
+    {
  21
+        private readonly XmlDocument dom = new XmlDocument();
  22
+
  23
+        /// <summary>
  24
+        /// Where did we find the configuration file?
  25
+        /// </summary>
  26
+        public readonly string BasePath;
  27
+
  28
+        public static string ExecutablePath
  29
+        {
  30
+            get
  31
+            {
  32
+                // this returns the executable name as given by the calling process, so
  33
+                // it needs to be absolutized.
  34
+                string p = Environment.GetCommandLineArgs()[0];
  35
+                return Path.Combine(Environment.CurrentDirectory, p);
  36
+
  37
+            }
  38
+        }
  39
+
  40
+        public ServiceDescriptor()
  41
+        {
  42
+            // find co-located configuration xml. We search up to the ancestor directories to simplify debugging,
  43
+            // as well as trimming off ".vshost" suffix (which is used during debugging)
  44
+            string p = ExecutablePath;
  45
+            string baseName = Path.GetFileNameWithoutExtension(p);
  46
+            if (baseName.EndsWith(".vshost")) baseName = baseName.Substring(0, baseName.Length - 7);
  47
+            while (true)
  48
+            {
  49
+                p = Path.GetDirectoryName(p);
  50
+                if (File.Exists(Path.Combine(p, baseName + ".xml")))
  51
+                    break;
  52
+            }
  53
+
  54
+            // register the base directory as environment variable so that future expansions can refer to this.
  55
+            Environment.SetEnvironmentVariable("BASE", p);
  56
+
  57
+            BasePath = Path.Combine(p, baseName);
  58
+
  59
+            dom.Load(BasePath+".xml");
  60
+        }
  61
+
  62
+        private string SingleElement(string tagName)
  63
+        {
  64
+            var n = dom.SelectSingleNode("//" + tagName);
  65
+            if (n == null) throw new InvalidDataException("<" + tagName + "> is missing in configuration XML");
  66
+            return Environment.ExpandEnvironmentVariables(n.InnerText);
  67
+        }
  68
+
  69
+        /// <summary>
  70
+        /// Path to the executable.
  71
+        /// </summary>
  72
+        public string Executable
  73
+        {
  74
+            get
  75
+            {
  76
+                return SingleElement("executable");
  77
+            }
  78
+        }
  79
+
  80
+        /// <summary>
  81
+        /// Arguments
  82
+        /// </summary>
  83
+        public string Arguments
  84
+        {
  85
+            get
  86
+            {
  87
+                return SingleElement("arguments");
  88
+            }
  89
+        }
  90
+
  91
+        public string Id
  92
+        {
  93
+            get
  94
+            {
  95
+                return SingleElement("id");
  96
+            }
  97
+        }
  98
+
  99
+        public string Caption
  100
+        {
  101
+            get
  102
+            {
  103
+                return SingleElement("name");
  104
+            }
  105
+        }
  106
+
  107
+        public string Description
  108
+        {
  109
+            get
  110
+            {
  111
+                return SingleElement("description");
  112
+            }
  113
+        }
  114
+
  115
+        /// <summary>
  116
+        /// True if the service can interact with the desktop.
  117
+        /// </summary>
  118
+        public bool Interactive
  119
+        {
  120
+            get
  121
+            {
  122
+                return dom.SelectSingleNode("//interactive") != null;
  123
+            }
  124
+        }
  125
+
  126
+        /// <summary>
  127
+        /// Environment variable overrides
  128
+        /// </summary>
  129
+        public Dictionary<string, string> EnvironmentVariables
  130
+        {
  131
+            get
  132
+            {
  133
+                Dictionary<string, string> map = new Dictionary<string, string>();
  134
+                foreach (XmlNode n in dom.SelectNodes("//env"))
  135
+                {
  136
+                    map[n.Attributes["name"].Value] = Environment.ExpandEnvironmentVariables(n.Attributes["value"].Value);
  137
+                }
  138
+                return map;
  139
+            }
  140
+        }
  141
+    }
  142
+
  143
+    public class WrapperService : ServiceBase
  144
+    {
  145
+        private Process process = new Process();
  146
+        private ServiceDescriptor descriptor;
  147
+
  148
+        /// <summary>
  149
+        /// Indicates to the watch dog thread that we are going to terminate the process,
  150
+        /// so don't try to kill us when the child exits.
  151
+        /// </summary>
  152
+        private bool orderlyShutdown;
  153
+
  154
+        public WrapperService()
  155
+        {
  156
+            this.descriptor = new ServiceDescriptor();
  157
+            this.ServiceName = descriptor.Id;
  158
+            this.CanStop = true;
  159
+            this.CanPauseAndContinue = false;
  160
+            this.AutoLog = true;
  161
+        }
  162
+
  163
+        /// <summary>
  164
+        /// Copy stuff from StreamReader to StreamWriter
  165
+        /// </summary>
  166
+        private void CopyStream(StreamReader i, StreamWriter o)
  167
+        {
  168
+            char[] buf = new char[1024];
  169
+            while (true)
  170
+            {
  171
+                int sz = i.Read(buf, 0, buf.Length);
  172
+                if (sz == 0) break;
  173
+                o.Write(buf, 0, sz);
  174
+                o.Flush();
  175
+            }
  176
+            i.Close();
  177
+            o.Close();
  178
+        }
  179
+
  180
+        protected override void OnStart(string[] args)
  181
+        {
  182
+            EventLog.WriteEntry("Starting "+descriptor.Executable+' '+descriptor.Arguments);
  183
+            string baseName = descriptor.BasePath;
  184
+
  185
+            var ps = process.StartInfo;
  186
+            ps.FileName = descriptor.Executable;
  187
+            ps.Arguments = descriptor.Arguments;
  188
+            ps.CreateNoWindow = false;
  189
+            ps.UseShellExecute = false;
  190
+            ps.RedirectStandardInput = true; // this creates a pipe for stdin to the new process, instead of having it inherit our stdin.
  191
+            ps.RedirectStandardOutput = true;
  192
+            ps.RedirectStandardError = true;
  193
+
  194
+            var envs = descriptor.EnvironmentVariables;
  195
+            foreach (string key in envs.Keys)
  196
+                ps.EnvironmentVariables[key] = envs[key];
  197
+
  198
+            process.Start();
  199
+
  200
+            // send stdout and stderr to its respective output file.
  201
+            new Thread(delegate() { CopyStream(process.StandardOutput, new StreamWriter(new FileStream(baseName + ".out.log", FileMode.Append))); }).Start();
  202
+            new Thread(delegate() { CopyStream(process.StandardError, new StreamWriter(new FileStream(baseName + ".err.log", FileMode.Append))); }).Start();
  203
+
  204
+            // monitor the completion of the process
  205
+            new Thread(delegate()
  206
+            {
  207
+                process.WaitForExit();
  208
+                if (!orderlyShutdown)
  209
+                {
  210
+                    EventLog.WriteEntry("Child process terminated with " + process.ExitCode,EventLogEntryType.Warning);
  211
+                    Environment.Exit(process.ExitCode);
  212
+                }
  213
+            }).Start();
  214
+
  215
+            process.StandardInput.Close(); // nothing for you to read!
  216
+        }
  217
+
  218
+        protected override void OnStop()
  219
+        {
  220
+            try
  221
+            {
  222
+                EventLog.WriteEntry("Stopping "+descriptor.Id);
  223
+                orderlyShutdown = true;
  224
+                process.Kill();
  225
+            }
  226
+            catch (InvalidOperationException)
  227
+            {
  228
+                // already terminated
  229
+            }
  230
+            process.Dispose();
  231
+        }
  232
+
  233
+
  234
+
  235
+        public static int Main(string[] args)
  236
+        {
  237
+            try
  238
+            {
  239
+                Run(args);
  240
+                return 0;
  241
+            }
  242
+            catch (WmiException e)
  243
+            {
  244
+                Console.Error.WriteLine(e);
  245
+                return (int)e.ErrorCode;
  246
+            }
  247
+            catch (Exception e)
  248
+            {
  249
+                Console.Error.WriteLine(e);
  250
+                return -1;
  251
+            }
  252
+        }
  253
+
  254
+        private static void ThrowNoSuchService()
  255
+        {
  256
+            throw new WmiException(ReturnValue.NoSuchService);
  257
+        }
  258
+
  259
+        public static void Run(string[] args)
  260
+        {
  261
+            if (args.Length > 0)
  262
+            {
  263
+                var d = new ServiceDescriptor();
  264
+                Win32Services svc = new WmiRoot().GetCollection<Win32Services>();
  265
+                Win32Service s = svc.Select(d.Id);
  266
+
  267
+                args[0] = args[0].ToLower();
  268
+                if (args[0] == "install")
  269
+                {
  270
+                    svc.Create(
  271
+                        d.Id,
  272
+                        d.Caption,
  273
+                        ServiceDescriptor.ExecutablePath,
  274
+                        WMI.ServiceType.OwnProcess,
  275
+                        ErrorControl.UserNotified,
  276
+                        StartMode.Automatic,
  277
+                        d.Interactive);
  278
+                    // update the description
  279
+                    /* Somehow this doesn't work, even though it doesn't report an error
  280
+                    Win32Service s = svc.Select(d.Id);
  281
+                    s.Description = d.Description;
  282
+                    s.Commit();
  283
+                     */
  284
+
  285
+                    // so using a classic method to set the description. Ugly.
  286
+                    Registry.LocalMachine.OpenSubKey("System").OpenSubKey("CurrentControlSet").OpenSubKey("Services")
  287
+                        .OpenSubKey(d.Id, true).SetValue("Description", d.Description);
  288
+                }
  289
+                if (args[0] == "uninstall")
  290
+                {
  291
+                    if (s == null)
  292
+                        return; // there's no such service, so consider it already uninstalled
  293
+                    try
  294
+                    {
  295
+                        s.Delete();
  296
+                    }
  297
+                    catch (WmiException e)
  298
+                    {
  299
+                        if (e.ErrorCode == ReturnValue.ServiceMarkedForDeletion)
  300
+                            return; // it's already uninstalled, so consider it a success
  301
+                        throw e;
  302
+                    }
  303
+                }
  304
+                if (args[0] == "start")
  305
+                {
  306
+                    if (s == null) ThrowNoSuchService();
  307
+                    s.StartService();
  308
+                }
  309
+                if (args[0] == "stop")
  310
+                {
  311
+                    if (s == null) ThrowNoSuchService();
  312
+                    s.StopService();
  313
+                }
  314
+                if (args[0] == "status")
  315
+                {
  316
+                    if (s == null)
  317
+                        Console.WriteLine("NonExistent");
  318
+                    else if (s.Started)
  319
+                        Console.WriteLine("Started");
  320
+                    else
  321
+                        Console.WriteLine("Stopped");
  322
+                }
  323
+                if (args[0] == "test")
  324
+                {
  325
+                    WrapperService wsvc = new WrapperService();
  326
+                    wsvc.OnStart(args);
  327
+                    Thread.Sleep(1000);
  328
+                    wsvc.OnStop();
  329
+                }
  330
+                return;
  331
+            }
  332
+            ServiceBase.Run(new WrapperService());
  333
+        }
  334
+    }
  335
+}
33  Properties/AssemblyInfo.cs
... ...
@@ -0,0 +1,33 @@
  1
+using System.Reflection;
  2
+using System.Runtime.CompilerServices;
  3
+using System.Runtime.InteropServices;
  4
+
  5
+[assembly: AssemblyTitle("Windows Service Wrapper")]
  6
+[assembly: AssemblyDescription("Allows arbitrary process to run as a Windows service by wrapping it")]
  7
+[assembly: AssemblyConfiguration("")]
  8
+[assembly: AssemblyCompany("Sun Microsystems, Inc.")]
  9
+[assembly: AssemblyProduct("Windows Service Wrapper")]
  10
+[assembly: AssemblyCopyright("Copyright 2008 Sun Micorsystems, Inc.")]
  11
+[assembly: AssemblyTrademark("")]
  12
+[assembly: AssemblyCulture("")]
  13
+
  14
+// Setting ComVisible to false makes the types in this assembly not visible 
  15
+// to COM components.  If you need to access a type in this assembly from 
  16
+// COM, set the ComVisible attribute to true on that type.
  17
+[assembly: ComVisible(false)]
  18
+
  19
+// The following GUID is for the ID of the typelib if this project is exposed to COM
  20
+[assembly: Guid("59ce18df-cacb-4360-bb80-798bd6459ca3")]
  21
+
  22
+// Version information for an assembly consists of the following four values:
  23
+//
  24
+//      Major Version
  25
+//      Minor Version 
  26
+//      Build Number
  27
+//      Revision
  28
+//
  29
+// You can specify all the values or you can default the Build and Revision Numbers 
  30
+// by using the '*' as shown below:
  31
+// [assembly: AssemblyVersion("1.0.*")]
  32
+[assembly: AssemblyVersion("1.0.0.0")]
  33
+[assembly: AssemblyFileVersion("1.0.0.0")]
216  Wmi.cs
... ...
@@ -0,0 +1,216 @@
  1
+using System;
  2
+using System.Collections.Generic;
  3
+using System.Text;
  4
+using System.Reflection;
  5
+using System.Management;
  6
+using DynamicProxy;
  7
+
  8
+namespace WMI
  9
+{
  10
+    //Reference: http://msdn2.microsoft.com/en-us/library/aa389390(VS.85).aspx
  11
+
  12
+    public enum ReturnValue
  13
+    {
  14
+        Success = 0,
  15
+        NotSupported = 1,
  16
+        AccessDenied = 2,
  17
+        DependentServicesRunning = 3,
  18
+        InvalidServiceControl = 4,
  19
+        ServiceCannotAcceptControl = 5,
  20
+        ServiceNotActive = 6,
  21
+        ServiceRequestTimeout = 7,
  22
+        UnknownFailure = 8,
  23
+        PathNotFound = 9,
  24
+        ServiceAlreadyRunning = 10,
  25
+        ServiceDatabaseLocked = 11,
  26
+        ServiceDependencyDeleted = 12,
  27
+        ServiceDependencyFailure = 13,
  28
+        ServiceDisabled = 14,
  29
+        ServiceLogonFailure = 15,
  30
+        ServiceMarkedForDeletion = 16,
  31
+        ServiceNoThread = 17,
  32
+        StatusCircularDependency = 18,
  33
+        StatusDuplicateName = 19,
  34
+        StatusInvalidName = 20,
  35
+        StatusInvalidParameter = 21,
  36
+        StatusInvalidServiceAccount = 22,
  37
+        StatusServiceExists = 23,
  38
+        ServiceAlreadyPaused = 24,
  39
+
  40
+        NoSuchService = 200
  41
+    }
  42
+
  43
+    /// <summary>
  44
+    /// Signals a problem in WMI related operations
  45
+    /// </summary>
  46
+    public class WmiException : Exception
  47
+    {
  48
+        public readonly ReturnValue ErrorCode;
  49
+
  50
+        public WmiException(string msg, ReturnValue code)
  51
+            : base(msg)
  52
+        {
  53
+            ErrorCode = code;
  54
+        }
  55
+
  56
+        public WmiException(ReturnValue code)
  57
+            : this(code.ToString(), code)
  58
+        {
  59
+        }
  60
+    }
  61
+
  62
+    /// <summary>
  63
+    /// Associated a WMI class name to the proxy interface (which should extend from IWmiCollection)
  64
+    /// </summary>
  65
+    public class WmiClassName : Attribute
  66
+    {
  67
+        public readonly string Name;
  68
+        public WmiClassName(string name) { this.Name = name; }
  69
+    }
  70
+
  71
+    /// <summary>
  72
+    /// Marker interface to denote a collection in WMI.
  73
+    /// </summary>
  74
+    public interface IWmiCollection {}
  75
+
  76
+    /// <summary>
  77
+    /// Marker interface to denote an individual managed object
  78
+    /// </summary>
  79
+    public interface IWmiObject
  80
+    {
  81
+        /// <summary>
  82
+        /// Reflect updates made to this object to the WMI provider.
  83
+        /// </summary>
  84
+        void Commit();
  85
+    }
  86
+
  87
+    public class WmiRoot
  88
+    {
  89
+        private readonly ManagementScope scope;
  90
+
  91
+        public WmiRoot() : this(null) { }
  92
+
  93
+        public WmiRoot(string machineName)
  94
+        {
  95
+            ConnectionOptions options = new ConnectionOptions();
  96
+
  97
+            string path;
  98
+
  99
+            if (machineName != null)
  100
+                path = String.Format(@"\\{0}\root\cimv2", machineName);
  101
+            else
  102
+                path = @"\root\cimv2";
  103
+            scope = new ManagementScope(path, options);
  104
+            scope.Connect();
  105
+        }
  106
+
  107
+        private static string capitalize(string s)
  108
+        {
  109
+            return char.ToUpper(s[0]) + s.Substring(1);
  110
+        }
  111
+
  112
+        abstract class BaseHandler : IProxyInvocationHandler
  113
+        {
  114
+            public abstract object Invoke(object proxy, MethodInfo method, object[] args);
  115
+
  116
+            protected void CheckError(ManagementBaseObject result)
  117
+            {
  118
+                int code = Convert.ToInt32(result["returnValue"]);
  119
+                if (code != 0)
  120
+                    throw new WmiException((ReturnValue)code);
  121
+            }
  122
+        }
  123
+
  124
+        class InstanceHandler : BaseHandler, IWmiObject
  125
+        {
  126
+            private readonly ManagementObject mo;
  127
+
  128
+            public InstanceHandler(ManagementObject o) { this.mo = o; }
  129
+
  130
+            public override object Invoke(object proxy, MethodInfo method, object[] args)
  131
+            {
  132
+                if (method.DeclaringType == typeof(IWmiObject))
  133
+                {
  134
+                    return method.Invoke(this, args);
  135
+                }
  136
+
  137
+                // TODO: proper property support
  138
+                if (method.Name.StartsWith("set_"))
  139
+                {
  140
+                    mo[method.Name.Substring(4)] = args[0];
  141
+                    return null;
  142
+                }
  143
+                if (method.Name.StartsWith("get_"))
  144
+                {
  145
+                    return mo[method.Name.Substring(4)];
  146
+                }
  147
+
  148
+                // method invocations
  149
+                ParameterInfo[] methodArgs = method.GetParameters();
  150
+
  151
+                ManagementBaseObject wmiArgs = mo.GetMethodParameters(method.Name);
  152
+                for (int i = 0; i < args.Length; i++)
  153
+                    wmiArgs[capitalize(methodArgs[i].Name)] = args[i];
  154
+
  155
+                CheckError(mo.InvokeMethod(method.Name, wmiArgs, null));
  156
+                return null;
  157
+            }
  158
+
  159
+            public void Commit()
  160
+            {
  161
+                mo.Put();
  162
+            }
  163
+        }
  164
+
  165
+        class ClassHandler : BaseHandler
  166
+        {
  167
+            private readonly ManagementClass mc;
  168
+            private readonly string wmiClass;
  169
+
  170
+            public ClassHandler(ManagementClass mc, string wmiClass) { this.mc = mc; this.wmiClass = wmiClass; }
  171
+
  172
+            public override object Invoke(object proxy, MethodInfo method, object[] args)
  173
+            {
  174
+                ParameterInfo[] methodArgs = method.GetParameters();
  175
+
  176
+                if (method.Name.StartsWith("Select"))
  177
+                {
  178
+                    // select method to find instances
  179
+                    string query = "SELECT * FROM " + wmiClass + " WHERE ";
  180
+                    for (int i = 0; i < args.Length; i++)
  181
+                    {
  182
+                        if (i != 0) query += " AND ";
  183
+                        query += ' ' + capitalize(methodArgs[i].Name) + " = '" + args[i] + "'";
  184
+                    }
  185
+
  186
+                    ManagementObjectSearcher searcher = new ManagementObjectSearcher(mc.Scope, new ObjectQuery(query));
  187
+                    ManagementObjectCollection results = searcher.Get();
  188
+                    // TODO: support collections
  189
+                    foreach (ManagementObject manObject in results)
  190
+                        return ProxyFactory.GetInstance().Create(new InstanceHandler(manObject), method.ReturnType, true);
  191
+                    return null;
  192
+                }
  193
+
  194
+                ManagementBaseObject wmiArgs = mc.GetMethodParameters(method.Name);
  195
+                for (int i = 0; i < args.Length; i++)
  196
+                    wmiArgs[capitalize(methodArgs[i].Name)] = args[i];
  197
+
  198
+                CheckError(mc.InvokeMethod(method.Name, wmiArgs, null));
  199
+                return null;
  200
+            }
  201
+        }
  202
+
  203
+        /// <summary>
  204
+        /// Obtains an object that corresponds to a table in WMI, which is a collection of a managed object.
  205
+        /// </summary>
  206
+        public T GetCollection<T>() where T : IWmiCollection
  207
+        {
  208
+            WmiClassName cn = (WmiClassName)typeof(T).GetCustomAttributes(typeof(WmiClassName), false)[0];
  209
+
  210
+            ObjectGetOptions getOptions = new ObjectGetOptions();
  211
+            ManagementPath path = new ManagementPath(cn.Name);
  212
+            ManagementClass manClass = new ManagementClass(scope, path, getOptions);
  213
+            return (T)ProxyFactory.GetInstance().Create(new ClassHandler(manClass, cn.Name), typeof(T), true);
  214
+        }
  215
+    }
  216
+}
64  WmiSchema.cs
... ...
@@ -0,0 +1,64 @@
  1
+
  2
+namespace WMI
  3
+{
  4
+    public enum ServiceType
  5
+    {
  6
+        KernalDriver = 1,
  7
+        FileSystemDriver = 2,
  8
+        Adapter = 4,
  9
+        RecognizerDriver = 8,
  10
+        OwnProcess = 16,
  11
+        ShareProcess = 32,
  12
+        InteractiveProcess = 256,
  13
+    }
  14
+
  15
+    public enum ErrorControl
  16
+    {
  17
+        UserNotNotified = 0,
  18
+        UserNotified = 1,
  19
+        SystemRestartedWithLastKnownGoodConfiguration = 2,
  20
+        SystemAttemptsToStartWithAGoodConfiguration = 3
  21
+    }
  22
+
  23
+    public enum StartMode
  24
+    {
  25
+        /// <summary>
  26
+        /// Device driver started by the operating system loader. This value is valid only for driver services.
  27
+        /// </summary>
  28
+        Boot,
  29
+        /// <summary>
  30
+        /// Device driver started by the operating system initialization process. This value is valid only for driver services.
  31
+        /// </summary>
  32
+        System,
  33
+        /// <summary>
  34
+        /// Service to be started automatically by the Service Control Manager during system startup.
  35
+        /// </summary>
  36
+        Automatic,
  37
+        /// <summary>
  38
+        /// Service to be started by the Service Control Manager when a process calls the StartService method.
  39
+        /// </summary>
  40
+        Manual,
  41
+        /// <summary>
  42
+        /// Service that can no longer be started.
  43
+        /// </summary>
  44
+        Disabled,
  45
+    }
  46
+
  47
+    [WmiClassName("Win32_Service")]
  48
+    public interface Win32Services : IWmiCollection
  49
+    {
  50
+        // ReturnValue Create(bool desktopInteract, string displayName, int errorControl, string loadOrderGroup, string loadOrderGroupDependencies, string name, string pathName, string serviceDependencies, string serviceType, string startMode, string startName, string startPassword);
  51
+        void Create(string name, string displayName, string pathName, ServiceType serviceType, ErrorControl errorControl, StartMode startMode, bool desktopInteract);
  52
+
  53
+        Win32Service Select(string name);
  54
+    }
  55
+
  56
+    public interface Win32Service : IWmiObject
  57
+    {
  58
+        string Description { get; set; }
  59
+        bool Started { get; }
  60
+        void Delete();
  61
+        void StartService();
  62
+        void StopService();
  63
+    }
  64
+}
63  pom.xml
... ...
@@ -0,0 +1,63 @@
  1
+<project xmlns="http://maven.apache.org/POM/4.0.0"
  2
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  4
+  <modelVersion>4.0.0</modelVersion>
  5
+  <groupId>com.sun.winsw</groupId>
  6
+  <artifactId>winsw</artifactId>
  7
+  <packaging>pom</packaging>
  8
+  <version>1.0</version>
  9
+  <name>Windows service wrapper</name>
  10
+    
  11
+   <distributionManagement>
  12
+    <repository>
  13
+      <id>java.net-m2-repository</id>
  14
+      <url>java-net:/maven2-repository/trunk/www/repository/</url>
  15
+    </repository>
  16
+  </distributionManagement>
  17
+
  18
+  <build>
  19
+    <plugins>
  20
+      <!-- fake out maven and install the binary artifact -->
  21
+      <plugin>
  22
+        <groupId>org.jvnet.maven-antrun-extended-plugin</groupId>
  23
+        <artifactId>maven-antrun-extended-plugin</artifactId>
  24
+        <executions>
  25
+          <execution>
  26
+            <phase>package</phase>
  27
+            <goals>
  28
+              <goal>run</goal>
  29
+            </goals>
  30
+            <configuration>
  31
+              <tasks>
  32
+                <attachArtifact file="bin/Debug/winsw.exe" type="exe" classifier="bin" />
  33
+              </tasks>
  34
+            </configuration>
  35
+          </execution>
  36
+        </executions>
  37
+      </plugin>
  38
+    </plugins>
  39
+    <extensions>
  40
+      <extension>
  41
+        <groupId>org.jvnet.wagon-svn</groupId>
  42
+        <artifactId>wagon-svn</artifactId>
  43
+        <version>1.8</version>
  44
+      </extension>
  45
+    </extensions>
  46
+  </build>
  47
+  
  48
+  <repositories>
  49
+    <repository>
  50
+      <id>maven2-repository.dev.java.net</id>
  51
+      <name>Java.net Repository for Maven</name>
  52
+      <url>http://download.java.net/maven/2/</url>
  53
+    </repository>
  54
+  </repositories>
  55
+
  56
+  <pluginRepositories>
  57
+    <pluginRepository>
  58
+      <id>maven2-repository.dev.java.net</id>
  59
+      <name>Java.net Repository for Maven</name>
  60
+      <url>http://download.java.net/maven/2/</url>
  61
+    </pluginRepository>
  62
+  </pluginRepositories>
  63
+</project>
64  winsw.csproj
... ...
@@ -0,0 +1,64 @@
  1
+<?xml version="1.0" encoding="utf-8"?>
  2
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  3
+  <PropertyGroup>
  4
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
  5
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
  6
+    <ProductVersion>9.0.21022</ProductVersion>
  7
+    <SchemaVersion>2.0</SchemaVersion>
  8
+    <ProjectGuid>{0DE77F55-ADE5-43C1-999A-0BC81153B039}</ProjectGuid>
  9
+    <OutputType>Exe</OutputType>
  10
+    <AppDesignerFolder>Properties</AppDesignerFolder>
  11
+    <RootNamespace>winsw</RootNamespace>
  12
+    <AssemblyName>winsw</AssemblyName>
  13
+    <TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
  14
+    <FileAlignment>512</FileAlignment>
  15
+    <StartupObject>
  16
+    </StartupObject>
  17
+  </PropertyGroup>
  18
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
  19
+    <DebugSymbols>true</DebugSymbols>
  20
+    <DebugType>full</DebugType>
  21
+    <Optimize>false</Optimize>
  22
+    <OutputPath>bin\Debug\</OutputPath>
  23
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
  24
+    <ErrorReport>prompt</ErrorReport>
  25
+    <WarningLevel>4</WarningLevel>
  26
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
  27
+  </PropertyGroup>
  28
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
  29
+    <DebugType>pdbonly</DebugType>
  30
+    <Optimize>true</Optimize>
  31
+    <OutputPath>bin\Release\</OutputPath>
  32
+    <DefineConstants>TRACE</DefineConstants>
  33
+    <ErrorReport>prompt</ErrorReport>
  34
+    <WarningLevel>4</WarningLevel>
  35
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
  36
+  </PropertyGroup>
  37
+  <ItemGroup>
  38
+    <Reference Include="System" />
  39
+    <Reference Include="System.Management" />
  40
+    <Reference Include="System.Data" />
  41
+    <Reference Include="System.ServiceProcess" />
  42
+    <Reference Include="System.Xml" />
  43
+  </ItemGroup>
  44
+  <ItemGroup>
  45
+    <Compile Include="DynamicProxy.cs" />
  46
+    <Compile Include="Main.cs">
  47
+      <SubType>Component</SubType>
  48
+    </Compile>
  49
+    <Compile Include="Properties\AssemblyInfo.cs" />
  50
+    <Compile Include="Wmi.cs" />
  51
+    <Compile Include="WmiSchema.cs" />
  52
+  </ItemGroup>
  53
+  <ItemGroup>
  54
+    <Content Include="winsw.xml" />
  55
+  </ItemGroup>
  56
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  57
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
  58
+       Other similar extension points exist, see Microsoft.Common.targets.
  59
+  <Target Name="BeforeBuild">
  60
+  </Target>
  61
+  <Target Name="AfterBuild">
  62
+  </Target>
  63
+  -->
  64
+</Project>
30  winsw.sln
... ...
@@ -0,0 +1,30 @@
  1
+
  2
+Microsoft Visual Studio Solution File, Format Version 10.00
  3
+# Visual Studio 2008
  4
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "winsw", "winsw.csproj", "{0DE77F55-ADE5-43C1-999A-0BC81153B039}"
  5
+EndProject
  6
+Global
  7
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
  8
+		Debug|Any CPU = Debug|Any CPU
  9
+		Debug|Mixed Platforms = Debug|Mixed Platforms
  10
+		Debug|Win32 = Debug|Win32
  11
+		Release|Any CPU = Release|Any CPU
  12
+		Release|Mixed Platforms = Release|Mixed Platforms
  13
+		Release|Win32 = Release|Win32
  14
+	EndGlobalSection
  15
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
  16
+		{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  17
+		{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Debug|Any CPU.Build.0 = Debug|Any CPU
  18
+		{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
  19
+		{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
  20
+		{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Debug|Win32.ActiveCfg = Debug|Any CPU
  21
+		{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Release|Any CPU.ActiveCfg = Release|Any CPU
  22
+		{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Release|Any CPU.Build.0 = Release|Any CPU
  23
+		{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
  24
+		{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Release|Mixed Platforms.Build.0 = Release|Any CPU
  25
+		{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Release|Win32.ActiveCfg = Release|Any CPU
  26
+	EndGlobalSection
  27
+	GlobalSection(SolutionProperties) = preSolution
  28
+		HideSolutionNode = FALSE
  29
+	EndGlobalSection
  30
+EndGlobal
7  winsw.xml
... ...
@@ -0,0 +1,7 @@
  1
+<configuration>
  2
+  <id>winsw</id>
  3
+  <name>Winsw test service(2)</name>
  4
+  <description>This service is a do-nothing test app. Really.</description>
  5
+  <executable>C:\development\jdk6u7\bin\java.exe</executable>
  6
+  <arguments>-classpath c:\cygwin\home\kohsuke\ws\hello-world\out\production\hello-world test.Main</arguments>
  7
+</configuration>

0 notes on commit e94628b

Please sign in to comment.
Something went wrong with that request. Please try again.