├── .gitignore
├── LICENSE
├── README.md
├── ServiceOnset.sln
├── ServiceOnset
├── 038.ico
├── AppHelper.cs
├── Config
│ ├── ServiceOnsetConfig.cs
│ ├── ServiceOnsetConfigDataContract.cs
│ └── ServiceRunModel.cs
├── Logger.cs
├── Program.cs
├── ProjectInstaller.Designer.cs
├── ProjectInstaller.cs
├── ProjectInstaller.resx
├── Properties
│ └── AssemblyInfo.cs
├── ServiceHost.Designer.cs
├── ServiceHost.cs
├── ServiceHost.resx
├── ServiceManager.cs
├── ServiceOnset.csproj
├── ServiceOnset.exe.json
├── Services
│ ├── DaemonService.cs
│ ├── IService.cs
│ ├── Interop.cs
│ ├── IntervalService.cs
│ ├── LaunchService.cs
│ ├── ServiceBase.cs
│ ├── ServiceFactory.cs
│ └── TimingService.cs
├── log4net.config
└── packages.config
└── packages
└── log4net.2.0.5
├── .signature.p7s
├── lib
├── net10-full
│ ├── log4net.dll
│ └── log4net.xml
├── net11-full
│ ├── log4net.dll
│ └── log4net.xml
├── net20-full
│ ├── log4net.dll
│ └── log4net.xml
├── net35-client
│ ├── log4net.dll
│ └── log4net.xml
├── net35-full
│ ├── log4net.dll
│ └── log4net.xml
├── net40-client
│ ├── log4net.dll
│ └── log4net.xml
├── net40-full
│ ├── log4net.dll
│ └── log4net.xml
└── net45-full
│ ├── log4net.dll
│ └── log4net.xml
└── log4net.2.0.5.nupkg
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.sln.docstates
8 | .vs/
9 |
10 | # Build results
11 |
12 | [Dd]ebug/
13 | [Rr]elease/
14 | x64/
15 | build/
16 | [Bb]in/
17 | [Oo]bj/
18 |
19 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
20 | !packages/*/build/
21 |
22 | # MSTest test Results
23 | [Tt]est[Rr]esult*/
24 | [Bb]uild[Ll]og.*
25 |
26 | *_i.c
27 | *_p.c
28 | *.ilk
29 | *.meta
30 | *.obj
31 | *.pch
32 | *.pdb
33 | *.pgc
34 | *.pgd
35 | *.rsp
36 | *.sbr
37 | *.tlb
38 | *.tli
39 | *.tlh
40 | *.tmp
41 | *.tmp_proj
42 | *.log
43 | *.vspscc
44 | *.vssscc
45 | .builds
46 | *.pidb
47 | *.log
48 | *.scc
49 |
50 | # Visual C++ cache files
51 | ipch/
52 | *.aps
53 | *.ncb
54 | *.opensdf
55 | *.sdf
56 | *.cachefile
57 |
58 | # Visual Studio profiler
59 | *.psess
60 | *.vsp
61 | *.vspx
62 |
63 | # Guidance Automation Toolkit
64 | *.gpState
65 |
66 | # ReSharper is a .NET coding add-in
67 | _ReSharper*/
68 | *.[Rr]e[Ss]harper
69 |
70 | # TeamCity is a build add-in
71 | _TeamCity*
72 |
73 | # DotCover is a Code Coverage Tool
74 | *.dotCover
75 |
76 | # NCrunch
77 | *.ncrunch*
78 | .*crunch*.local.xml
79 |
80 | # Installshield output folder
81 | [Ee]xpress/
82 |
83 | # DocProject is a documentation generator add-in
84 | DocProject/buildhelp/
85 | DocProject/Help/*.HxT
86 | DocProject/Help/*.HxC
87 | DocProject/Help/*.hhc
88 | DocProject/Help/*.hhk
89 | DocProject/Help/*.hhp
90 | DocProject/Help/Html2
91 | DocProject/Help/html
92 |
93 | # Click-Once directory
94 | publish/
95 |
96 | # Publish Web Output
97 | *.Publish.xml
98 |
99 | # NuGet Packages Directory
100 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
101 | #packages/
102 |
103 | # Windows Azure Build Output
104 | csx
105 | *.build.csdef
106 |
107 | # Windows Store app package directory
108 | AppPackages/
109 |
110 | # Others
111 | sql/
112 | *.Cache
113 | ClientBin/
114 | [Ss]tyle[Cc]op.*
115 | ~$*
116 | *~
117 | *.dbmdl
118 | *.[Pp]ublish.xml
119 | *.pfx
120 | *.publishsettings
121 |
122 | # RIA/Silverlight projects
123 | Generated_Code/
124 |
125 | # Backup & report files from converting an old project file to a newer
126 | # Visual Studio version. Backup files are not needed, because we have git ;-)
127 | _UpgradeReport_Files/
128 | Backup*/
129 | UpgradeLog*.XML
130 | UpgradeLog*.htm
131 |
132 | # SQL Server files
133 | App_Data/*.mdf
134 | App_Data/*.ldf
135 |
136 |
137 | #LightSwitch generated files
138 | GeneratedArtifacts/
139 | _Pvt_Extensions/
140 | ModelManifest.xml
141 |
142 | # =========================
143 | # Windows detritus
144 | # =========================
145 |
146 | # Windows image file caches
147 | Thumbs.db
148 | ehthumbs.db
149 |
150 | # Folder config file
151 | Desktop.ini
152 |
153 | # Recycle Bin used on file shares
154 | $RECYCLE.BIN/
155 |
156 | # Mac desktop service store files
157 | .DS_Store
158 |
159 | # Other exclusions
160 | Temp/
161 | Upload/
162 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright 2014 Hedda
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ServiceOnset - A light and FREE service wrapper
2 | ===============================================
3 |
4 | ServiceOnset is an exciting utility to help you to run **one or more programs** as **single windows service**. Is it cool?
5 | The most typical usage is for [Node.js], [COW], regular jobs and so on.
6 | ServiceOnset is a wrapper program implemented as a windows service. So it enables some of foreground applications started before login.
7 | Known [Issue #7]. ServiceOnset currently CANNOT support an UI-based program because of the **session 0 isolation** problem on Win2008 or above. Only added `Interop.cs` as a preparation for further enhancement.
8 |
9 | Prerequisites
10 | -------------
11 | Windows operation with [Microsoft .NET Framework 4.5.2]
12 |
13 | Installation
14 | ------------
15 | * Clone and build the solution with `VisualStudio`, or **[DOWNLOAD]** the binary package directly.
16 | > log4net.config
17 | > log4net.dll
18 | > ServiceOnset.exe
19 | > ServiceOnset.exe.json
20 |
21 | * Start a command line with Administrator privilege.
22 | * Navigate to the directory of the binary package.
23 | * Run `C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe ServiceOnset.exe` to install the service.
24 | * `Optional.` If you got an error like below ([Issue #3]), please `right-click` ServiceOnset.exe file and click `Unblock` button in the program's property window.
25 | > Exception occurred while initializing the installation:
26 | > System.IO.FileLoadException: Could not load file or assembly '...\ServiceOnset.exe' or one of its dependencies. Operation is not supported. (Exception from HRESULT: 0x80131515).
27 |
28 | * Change the config of ServiceOnset as you want. Refer to [ServiceOnset.exe.json](#config)
29 | * `Optional.` Change the config of log4net if you want assign a dedicated logger for a service. Refer to [log4net Config]
30 |
31 | ```xml
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | ```
46 |
47 | * `Optional.` If you are trying to launch a desktop-related program, you may need to create these 2 folders.
48 | > C:\Windows\SysWOW64\config\systemprofile\Desktop
49 | > C:\Windows\System32\config\systemprofile\Desktop
50 |
51 | * Open windows services manager and start **ServiceOnset** service. Or directly execute `net start ServiceOnset`.
52 | * Enjoy ~
53 |
54 | Uninstallation
55 | --------------
56 | 1. Stop the service `net stop ServiceOnset` if it is running.
57 | 2. Start a command line with Administrator privilege.
58 | 3. Navigate to the directory of the binary package.
59 | 4. Run `C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe /u ServiceOnset.exe` to remove the service.
60 | 5. Clean up the directory.
61 |
62 | ServiceOnset.exe.json
63 | ------------------------------------------
64 | #### Sample
65 | ```json
66 | {
67 | "enableLog": true,
68 | "services": [
69 | {
70 | "disable": false,
71 | "name": "Ping-Baidu",
72 | "desc": "Termly PING www.baidu.com",
73 | "command": "ping",
74 | "arguments": "www.baidu.com",
75 | "workingDirectory": "",
76 | "runMode": "interval",
77 | "intervalInSeconds": 30,
78 | "timingExp": "00",
79 | "useShellExecute": false,
80 | "hideWindow": false,
81 | "killExistingProcess": false,
82 | "enableLog": true
83 | }
84 | ]
85 | }
86 | ```
87 | #### References
88 | |Property |Value type |Required |Default |Description|
89 | |-------- |------ |------- |------- |-----------|
90 | |enableLog |bool | |false |Determinate if generate logs by `log4net`.|
91 | |services |array |Yes |(empty) |Program definitions hosted by `ServiceOnset`.|
92 | |disable |bool | |false |Determinate if the service is enabled.|
93 | |name |string |Yes | |Program identifier, must be same to the corresponding logger name.|
94 | |desc |string | | |Service description.|
95 | |command |string |Yes | |Command (with full path, relative path ([Issue #5]) or Windows ENV path). eg.: `ping`.|
96 | |arguments |string | |"" |Command arguments. eg.: `www.baidu.com`.|
97 | |workingDirectory |string | |Command path, or ServiceOnset path |It represents the startup path of the command. eg.: `D:\\ServiceOnset\\`.|
98 | |runMode |enum | |"daemon" |`"daemon"`: Auto-restart the program if it exited.
`"launch"`: Launch the program once and let it be.
`"interval"`: Restart the program termly by force kill the running process.
`"timing"`: Check current time per 60 seconds, run the program when matching with timingExp.|
99 | |intervalInSeconds |int | |30 |Detecting interval in seconds for current run mode.|
100 | |timingExp |string | |"00" |Expression format: `MMddHHmm`. eg.: `01022300` - run at 23:00 on Jan 2nd of every year; `101330` - run at 13:30 on the 10th day of every month; `2205` - run at 22:05 of every day; `15` - run at the 15th minute of every hour.|
101 | |useShellExecute |bool | |false |Start a process by [UseShellExecute]. Will omit the standard output of a console when the value is `true`.|
102 | |hideWindow |bool | |false |Try to hide the window of the command.|
103 | |killExistingProcess|bool | |false |If `true`, will try to kill the existing process whose file name equals [Command] when initializing the service entry. Here any error will be ignored except logging.|
104 |
105 | Case sample for [COW]
106 | ---------------------
107 | 1. Make sure `cow.exe` or `cow-hide.exe` or `cow-taskbar.exe` with `rc.txt` can work.
108 | 2. Extract ServiceOnset binary package to COW directory. And config like this:
109 | ```json
110 | {
111 | "enableLog": true,
112 | "services": [
113 | {
114 | "name": "COW-Proxy",
115 | "command": "cow"
116 | }
117 | ]
118 | }
119 | ```
120 |
121 | -------------------------------
122 | **Contact QQ: 9812152 `@Hedda`**
123 |
124 | [Node.js]: http://nodejs.org/
125 | [COW]: https://github.com/cyfdecyf/cow
126 | [Microsoft .NET Framework 4.5.2]: http://www.microsoft.com/zh-cn/download/details.aspx?id=17718
127 | [DOWNLOAD]: https://github.com/HeddaZ/ServiceOnset/releases
128 | [log4net Config]: http://logging.apache.org/log4net/release/config-examples.html
129 | [UseShellExecute]: http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.useshellexecute.aspx
130 | [Issue #3]: https://github.com/HeddaZ/ServiceOnset/issues/3
131 | [Issue #5]: https://github.com/HeddaZ/ServiceOnset/issues/5
132 | [Issue #7]: https://github.com/HeddaZ/ServiceOnset/issues/7
--------------------------------------------------------------------------------
/ServiceOnset.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.31205.134
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceOnset", "ServiceOnset\ServiceOnset.csproj", "{EAB17BDC-DA14-4C48-9328-812DADB0B89F}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{190C38B8-D107-4891-B047-7F40F4DF4AD4}"
9 | ProjectSection(SolutionItems) = preProject
10 | LICENSE = LICENSE
11 | README.md = README.md
12 | EndProjectSection
13 | EndProject
14 | Global
15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
16 | Debug|Any CPU = Debug|Any CPU
17 | Release|Any CPU = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 | {EAB17BDC-DA14-4C48-9328-812DADB0B89F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {EAB17BDC-DA14-4C48-9328-812DADB0B89F}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {EAB17BDC-DA14-4C48-9328-812DADB0B89F}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {EAB17BDC-DA14-4C48-9328-812DADB0B89F}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {B9AE36C3-DCC5-4677-B28F-0386CFF19C6D}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/ServiceOnset/038.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeddaZ/ServiceOnset/5ca75494e6bb7438f5e13d375f7c9fc7899034bc/ServiceOnset/038.ico
--------------------------------------------------------------------------------
/ServiceOnset/AppHelper.cs:
--------------------------------------------------------------------------------
1 | using ServiceOnset.Config;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Reflection;
7 | using System.Text;
8 |
9 | [assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config")]
10 |
11 | namespace ServiceOnset
12 | {
13 | public class AppHelper
14 | {
15 | public const string AppTitle = "ServiceOnset";
16 | public const string AppDescription = "Run multiple programs in background upon single windows service. (QQ:9812152)";
17 |
18 | public static readonly string AppPath = Assembly.GetExecutingAssembly().Location;
19 | public static readonly string AppDirectory = Path.GetDirectoryName(AppPath);
20 | public static readonly string AppVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
21 |
22 | #region Log singleton
23 |
24 | private static Logger _log;
25 | private static object _logMutex = new object();
26 | public static Logger Log
27 | {
28 | get
29 | {
30 | if (_log == null)
31 | {
32 | lock (_logMutex)
33 | {
34 | if (_log == null)
35 | {
36 | _log = new Logger(Config.EnableLog);
37 | }
38 | }
39 | }
40 | return _log;
41 | }
42 | }
43 |
44 | #endregion
45 | #region Config singleton
46 |
47 | private static IServiceOnsetConfig _config;
48 | private static object _configMutex = new object();
49 | public static IServiceOnsetConfig Config
50 | {
51 | get
52 | {
53 | if (_config == null)
54 | {
55 | lock (_configMutex)
56 | {
57 | if (_config == null)
58 | {
59 | _config = ServiceOnsetConfig.Create(AppHelper.AppPath + ".json"); // ServiceOnset.exe.json
60 | }
61 | }
62 | }
63 | return _config;
64 | }
65 | }
66 |
67 | #endregion
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/ServiceOnset/Config/ServiceOnsetConfig.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Runtime.Serialization.Json;
6 | using System.Text;
7 | using System.Xml;
8 |
9 | namespace ServiceOnset.Config
10 | {
11 | public interface IServiceOnsetConfig
12 | {
13 | bool EnableLog { get; }
14 | IServiceStartInfo[] StartInfos { get; }
15 | }
16 | public partial class ServiceOnsetConfig : IServiceOnsetConfig
17 | {
18 | #region Creator
19 |
20 | public static ServiceOnsetConfig Create(string configPath, Encoding encoding)
21 | {
22 | string configString;
23 | using (StreamReader configReader = new StreamReader(configPath))
24 | {
25 | configString = configReader.ReadToEnd();
26 | }
27 | using (MemoryStream configStream = new MemoryStream(encoding.GetBytes(configString)))
28 | {
29 | DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(ServiceOnsetConfig));
30 | return (ServiceOnsetConfig)jsonSerializer.ReadObject(configStream);
31 | }
32 | }
33 | public static ServiceOnsetConfig Create(string configPath)
34 | {
35 | return ServiceOnsetConfig.Create(configPath, Encoding.UTF8);
36 | }
37 |
38 | #endregion
39 |
40 | private ServiceOnsetConfig()
41 | {
42 | }
43 |
44 | private bool? _enableLog;
45 | public bool EnableLog
46 | {
47 | get
48 | {
49 | if (!_enableLog.HasValue)
50 | {
51 | _enableLog = _originalEnableLog;
52 | }
53 | return _enableLog.Value;
54 | }
55 | }
56 |
57 | private IServiceStartInfo[] _startInfos;
58 | public IServiceStartInfo[] StartInfos
59 | {
60 | get
61 | {
62 | if (_startInfos == null)
63 | {
64 | if (_originalStartInfos == null)
65 | {
66 | throw new ArgumentNullException("services");
67 | }
68 | else
69 | {
70 | _startInfos = _originalStartInfos.OfType().ToArray();
71 | }
72 | }
73 | return _startInfos;
74 | }
75 | }
76 | }
77 |
78 | public interface IServiceStartInfo
79 | {
80 | bool Disable { get; }
81 | string Name { get; }
82 | string Description { get; }
83 | string Command { get; }
84 | string Arguments { get; }
85 | string WorkingDirectory { get; }
86 | ServiceRunMode RunMode { get; }
87 | int IntervalInSeconds { get; }
88 | string TimingExp { get; }
89 | bool UseShellExecute { get; }
90 | bool HideWindow { get; }
91 | bool KillExistingProcess { get; }
92 | bool EnableLog { get; }
93 | }
94 | public partial class ServiceStartInfo : IServiceStartInfo
95 | {
96 | private bool? _disable;
97 | public bool Disable
98 | {
99 | get
100 | {
101 | if (!_disable.HasValue)
102 | {
103 | _disable = _originalDisable;
104 | }
105 | return _disable.Value;
106 | }
107 | }
108 |
109 | private string _name;
110 | public string Name
111 | {
112 | get
113 | {
114 | if (_name == null)
115 | {
116 | if (string.IsNullOrWhiteSpace(_originalName))
117 | {
118 | throw new ArgumentNullException("name");
119 | }
120 | else
121 | {
122 | _name = _originalName;
123 | }
124 | }
125 | return _name;
126 | }
127 | }
128 |
129 | private string _description;
130 | public string Description
131 | {
132 | get
133 | {
134 | if (_description == null)
135 | {
136 | _description = _originalDescription;
137 | }
138 | return _description;
139 | }
140 | }
141 |
142 | private string _command;
143 | public string Command
144 | {
145 | get
146 | {
147 | if (_command == null)
148 | {
149 | if (string.IsNullOrWhiteSpace(_originalCommand))
150 | {
151 | throw new ArgumentNullException("command");
152 | }
153 | else if (Path.IsPathRooted(_originalCommand))
154 | {
155 | _command = _originalCommand;
156 | }
157 | else
158 | {
159 | #region 非完整路径,尝试匹配程序路径、工作目录、系统路径
160 |
161 | string possibleCommand = Path.Combine(AppHelper.AppDirectory, _originalCommand);
162 | if (CommandExists(possibleCommand))
163 | {
164 | _command = possibleCommand;
165 | }
166 | else
167 | {
168 | if (string.IsNullOrWhiteSpace(_originalWorkingDirectory))
169 | {
170 | _command = _originalCommand;
171 | }
172 | else
173 | {
174 | possibleCommand = Path.IsPathRooted(_originalWorkingDirectory)
175 | ? Path.Combine(_originalWorkingDirectory, _originalCommand)
176 | : Path.Combine(AppHelper.AppDirectory, _originalWorkingDirectory, _originalCommand);
177 | _command = CommandExists(possibleCommand)
178 | ? possibleCommand
179 | : _originalCommand;
180 | }
181 | }
182 |
183 | #endregion
184 | }
185 | }
186 | return _command;
187 | }
188 | }
189 |
190 | private string _arguments;
191 | public string Arguments
192 | {
193 | get
194 | {
195 | if (_arguments == null)
196 | {
197 | _arguments = _originalArguments ?? string.Empty;
198 | }
199 | return _arguments;
200 | }
201 | }
202 |
203 | private string _workingDirectory;
204 | public string WorkingDirectory
205 | {
206 | get
207 | {
208 | if (_workingDirectory == null)
209 | {
210 | _workingDirectory = string.IsNullOrWhiteSpace(_originalWorkingDirectory)
211 | ? Path.GetDirectoryName(Command)
212 | : (Path.IsPathRooted(_originalWorkingDirectory)
213 | ? _originalWorkingDirectory
214 | : Path.Combine(AppHelper.AppDirectory, _originalWorkingDirectory));
215 | }
216 | return _workingDirectory;
217 | }
218 | }
219 |
220 | private ServiceRunMode? _runMode;
221 | public ServiceRunMode RunMode
222 | {
223 | get
224 | {
225 | if (!_runMode.HasValue)
226 | {
227 | if (int.TryParse(_originalRunMode, out int runModeValue))
228 | {
229 | _runMode = (ServiceRunMode)runModeValue;
230 | }
231 | else if (Enum.TryParse(_originalRunMode, true, out ServiceRunMode runModeEnum))
232 | {
233 | _runMode = runModeEnum;
234 | }
235 | else
236 | {
237 | _runMode = ServiceRunMode.Daemon;
238 | }
239 | }
240 | return _runMode.Value;
241 | }
242 | }
243 |
244 | private int? _intervalInSeconds;
245 | public int IntervalInSeconds
246 | {
247 | get
248 | {
249 | if (!_intervalInSeconds.HasValue)
250 | {
251 | _intervalInSeconds = _originalIntervalInSeconds <= 0
252 | ? 30
253 | : _originalIntervalInSeconds;
254 | }
255 | return _intervalInSeconds.Value;
256 | }
257 | }
258 |
259 | private string _timingExp;
260 | public string TimingExp
261 | {
262 | get
263 | {
264 | if (_timingExp == null)
265 | {
266 | _timingExp = string.IsNullOrWhiteSpace(_originalTimingExp)
267 | ? "00"
268 | : _originalTimingExp;
269 | }
270 | return _timingExp;
271 | }
272 | }
273 |
274 | private bool? _useShellExecute;
275 | public bool UseShellExecute
276 | {
277 | get
278 | {
279 | if (!_useShellExecute.HasValue)
280 | {
281 | _useShellExecute = _originalUseShellExecute;
282 | }
283 | return _useShellExecute.Value;
284 | }
285 | }
286 |
287 | private bool? _hideWindow;
288 | public bool HideWindow
289 | {
290 | get
291 | {
292 | if (!_hideWindow.HasValue)
293 | {
294 | _hideWindow = _originalHideWindow;
295 | }
296 | return _hideWindow.Value;
297 | }
298 | }
299 |
300 | private bool? _killExistingProcess;
301 | public bool KillExistingProcess
302 | {
303 | get
304 | {
305 | if (!_killExistingProcess.HasValue)
306 | {
307 | _killExistingProcess = _originalKillExistingProcess;
308 | }
309 | return _killExistingProcess.Value;
310 | }
311 | }
312 |
313 | private bool? _enableLog;
314 | public bool EnableLog
315 | {
316 | get
317 | {
318 | if (!_enableLog.HasValue)
319 | {
320 | _enableLog = _originalEnableLog;
321 | }
322 | return _enableLog.Value;
323 | }
324 | }
325 |
326 | private bool CommandExists(string command)
327 | {
328 | if (File.Exists(command))
329 | {
330 | return true;
331 | }
332 |
333 | string directory = Path.GetDirectoryName(command);
334 | if (Directory.Exists(directory))
335 | {
336 | string fileName = Path.GetFileNameWithoutExtension(command);
337 | return Directory.GetFiles(directory, fileName + ".*", SearchOption.TopDirectoryOnly).Length > 0;
338 | }
339 | else
340 | {
341 | return false;
342 | }
343 | }
344 | }
345 | }
346 |
--------------------------------------------------------------------------------
/ServiceOnset/Config/ServiceOnsetConfigDataContract.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Runtime.Serialization;
5 | using System.Text;
6 |
7 | namespace ServiceOnset.Config
8 | {
9 | [DataContract]
10 | public partial class ServiceOnsetConfig
11 | {
12 | [DataMember(Name = "enableLog")]
13 | private bool _originalEnableLog;
14 | [DataMember(Name = "services")]
15 | private ServiceStartInfo[] _originalStartInfos;
16 | }
17 |
18 | [DataContract]
19 | public partial class ServiceStartInfo
20 | {
21 | [DataMember(Name = "disable")]
22 | private bool _originalDisable;
23 | [DataMember(Name = "name")]
24 | private string _originalName;
25 | [DataMember(Name = "desc")]
26 | private string _originalDescription;
27 | [DataMember(Name = "command")]
28 | private string _originalCommand;
29 | [DataMember(Name = "arguments")]
30 | private string _originalArguments;
31 | [DataMember(Name = "workingDirectory")]
32 | private string _originalWorkingDirectory;
33 | [DataMember(Name = "runMode")]
34 | private string _originalRunMode;
35 | [DataMember(Name = "intervalInSeconds")]
36 | private int _originalIntervalInSeconds;
37 | [DataMember(Name = "timingExp")]
38 | private string _originalTimingExp;
39 | [DataMember(Name = "useShellExecute")]
40 | private bool _originalUseShellExecute;
41 | [DataMember(Name = "hideWindow")]
42 | private bool _originalHideWindow;
43 | [DataMember(Name = "killExistingProcess")]
44 | private bool _originalKillExistingProcess;
45 | [DataMember(Name = "enableLog")]
46 | private bool _originalEnableLog;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/ServiceOnset/Config/ServiceRunModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace ServiceOnset.Config
7 | {
8 | public enum ServiceRunMode : int
9 | {
10 | Daemon = 1,
11 | Launch = 2,
12 | Interval = 4,
13 | Timing = 8
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/ServiceOnset/Logger.cs:
--------------------------------------------------------------------------------
1 | using log4net;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 |
7 | namespace ServiceOnset
8 | {
9 | public class Logger
10 | {
11 | private const string RootLoggerName = "root";
12 |
13 | public ILog InnerLogger
14 | {
15 | get;
16 | private set;
17 | }
18 | public bool Enabled
19 | {
20 | get;
21 | set;
22 | }
23 |
24 | internal Logger(string name, bool enabled)
25 | {
26 | InnerLogger = LogManager.GetLogger(name);
27 | Enabled = enabled && AppHelper.Config.EnableLog;
28 | }
29 | internal Logger(bool enabled)
30 | : this(RootLoggerName, enabled)
31 | {
32 | }
33 |
34 | #region Logging
35 |
36 | public void Info(string format, params object[] args)
37 | {
38 | if (Enabled)
39 | {
40 | InnerLogger.InfoFormat(format, args);
41 | }
42 | }
43 | public void Info(string message)
44 | {
45 | if (Enabled)
46 | {
47 | InnerLogger.Info(message);
48 | }
49 | }
50 | public void Info(string message, Exception exception)
51 | {
52 | if (Enabled)
53 | {
54 | InnerLogger.Info(message, exception);
55 | }
56 | }
57 |
58 | public void Error(string format, params object[] args)
59 | {
60 | if (Enabled)
61 | {
62 | InnerLogger.ErrorFormat(format, args);
63 | }
64 | }
65 | public void Error(string message)
66 | {
67 | if (Enabled)
68 | {
69 | InnerLogger.Error(message);
70 | }
71 | }
72 | public void Error(string message, Exception exception)
73 | {
74 | if (Enabled)
75 | {
76 | InnerLogger.Error(message, exception);
77 | }
78 | }
79 |
80 | #endregion
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/ServiceOnset/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.ServiceProcess;
5 | using System.Text;
6 |
7 | namespace ServiceOnset
8 | {
9 | static class Program
10 | {
11 | static void Main()
12 | {
13 | ServiceBase.Run(new ServiceBase[]
14 | {
15 | new ServiceHost()
16 | });
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ServiceOnset/ProjectInstaller.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace ServiceOnset
2 | {
3 | partial class ProjectInstaller
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Component Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller();
32 | this.serviceInstaller1 = new System.ServiceProcess.ServiceInstaller();
33 | //
34 | // serviceProcessInstaller1
35 | //
36 | this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
37 | this.serviceProcessInstaller1.Password = null;
38 | this.serviceProcessInstaller1.Username = null;
39 | //
40 | // serviceInstaller1
41 | //
42 | this.serviceInstaller1.Description = AppHelper.AppDescription;
43 | this.serviceInstaller1.ServiceName = AppHelper.AppTitle;
44 | this.serviceInstaller1.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
45 | this.serviceInstaller1.Committed += new System.Configuration.Install.InstallEventHandler(this.serviceInstaller1_Committed);
46 | //
47 | // ProjectInstaller
48 | //
49 | this.Installers.AddRange(new System.Configuration.Install.Installer[] {
50 | this.serviceProcessInstaller1,
51 | this.serviceInstaller1});
52 |
53 | }
54 |
55 | #endregion
56 |
57 | private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller1;
58 | private System.ServiceProcess.ServiceInstaller serviceInstaller1;
59 | }
60 | }
--------------------------------------------------------------------------------
/ServiceOnset/ProjectInstaller.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.ComponentModel;
5 | using System.Configuration.Install;
6 | using System.Linq;
7 | using System.Management;
8 |
9 | namespace ServiceOnset
10 | {
11 | [RunInstaller(true)]
12 | public partial class ProjectInstaller : System.Configuration.Install.Installer
13 | {
14 | public ProjectInstaller()
15 | {
16 | InitializeComponent();
17 | }
18 |
19 | private void serviceInstaller1_Committed(object sender, InstallEventArgs e)
20 | {
21 | ConnectionOptions options = new ConnectionOptions();
22 | options.Impersonation = ImpersonationLevel.Impersonate;
23 | ManagementScope scope = new ManagementScope(@"root\CIMV2", options);
24 | scope.Connect();
25 |
26 | ManagementObject wmi = new ManagementObject("Win32_Service.Name='" + serviceInstaller1.ServiceName + "'");
27 | ManagementBaseObject inParam = wmi.GetMethodParameters("Change");
28 | inParam["DesktopInteract"] = true;
29 | ManagementBaseObject outParam = wmi.InvokeMethod("Change", inParam, null);
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/ServiceOnset/ProjectInstaller.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | 17, 17
122 |
123 |
124 | 208, 17
125 |
126 |
127 | False
128 |
129 |
--------------------------------------------------------------------------------
/ServiceOnset/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using ServiceOnset;
2 | using System.Reflection;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 |
6 | // General Information about an assembly is controlled through the following
7 | // set of attributes. Change these attribute values to modify the information
8 | // associated with an assembly.
9 | [assembly: AssemblyTitle(AppHelper.AppTitle)]
10 | [assembly: AssemblyDescription(AppHelper.AppDescription)]
11 | [assembly: AssemblyConfiguration("")]
12 | [assembly: AssemblyCompany("")]
13 | [assembly: AssemblyProduct(AppHelper.AppTitle)]
14 | [assembly: AssemblyCopyright("Copyright © Plusii 2022")]
15 | [assembly: AssemblyTrademark("")]
16 | [assembly: AssemblyCulture("")]
17 |
18 | // Setting ComVisible to false makes the types in this assembly not visible
19 | // to COM components. If you need to access a type in this assembly from
20 | // COM, set the ComVisible attribute to true on that type.
21 | [assembly: ComVisible(false)]
22 |
23 | // The following GUID is for the ID of the typelib if this project is exposed to COM
24 | [assembly: Guid("a7dc7161-b6aa-47d6-ba87-246289b305dc")]
25 |
26 | // Version information for an assembly consists of the following four values:
27 | //
28 | // Major Version
29 | // Minor Version
30 | // Build Number
31 | // Revision
32 | //
33 | // You can specify all the values or you can default the Build and Revision Numbers
34 | // by using the '*' as shown below:
35 | // [assembly: AssemblyVersion("1.0.*")]
36 | [assembly: AssemblyVersion("4.0.*")]
37 | [assembly: AssemblyFileVersion("4.0.0.0")]
38 |
--------------------------------------------------------------------------------
/ServiceOnset/ServiceHost.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace ServiceOnset
2 | {
3 | partial class ServiceHost
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Component Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | //
32 | // ServiceHost
33 | //
34 | this.ServiceName = AppHelper.AppTitle;
35 |
36 | }
37 |
38 | #endregion
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/ServiceOnset/ServiceHost.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Data;
5 | using System.Diagnostics;
6 | using System.Linq;
7 | using System.ServiceProcess;
8 | using System.Text;
9 |
10 | namespace ServiceOnset
11 | {
12 | public partial class ServiceHost : ServiceBase
13 | {
14 | private ServiceManager ServiceManager { get; set; }
15 |
16 | public ServiceHost()
17 | {
18 | InitializeComponent();
19 | ServiceManager = new ServiceManager(AppHelper.Config);
20 | }
21 |
22 | protected override void OnStart(string[] args)
23 | {
24 | ServiceManager.RunServices();
25 | }
26 |
27 | protected override void OnStop()
28 | {
29 | ServiceManager.StopServices();
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/ServiceOnset/ServiceHost.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | False
122 |
123 |
--------------------------------------------------------------------------------
/ServiceOnset/ServiceManager.cs:
--------------------------------------------------------------------------------
1 | using ServiceOnset.Config;
2 | using ServiceOnset.Services;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 |
8 | namespace ServiceOnset
9 | {
10 | public class ServiceManager
11 | {
12 | public List Services
13 | {
14 | get;
15 | private set;
16 | }
17 |
18 | public ServiceManager(IServiceOnsetConfig config)
19 | {
20 | Services = config.StartInfos
21 | .Where(s => !s.Disable)
22 | .Select(s => ServiceFactory.Instance.Create(s))
23 | .ToList();
24 | AppHelper.Log.Info("{0} service(s) initialized. {1}",
25 | Services.Count,
26 | string.Join(", ", Services.Select(s => s.StartInfo.Name + "(" + s.StartInfo.RunMode.ToString() + ")").ToArray()));
27 | }
28 |
29 | public void RunServices()
30 | {
31 | Services.ForEach(s => s.Start());
32 | AppHelper.Log.Info(AppHelper.AppTitle + " started");
33 | }
34 | public void StopServices()
35 | {
36 | Services.ForEach(s => s.Stop());
37 | AppHelper.Log.Info(AppHelper.AppTitle + " stopped");
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/ServiceOnset/ServiceOnset.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {EAB17BDC-DA14-4C48-9328-812DADB0B89F}
8 | WinExe
9 | Properties
10 | ServiceOnset
11 | ServiceOnset
12 | v4.5.2
13 | 512
14 | publish\
15 | true
16 | Disk
17 | false
18 | Foreground
19 | 7
20 | Days
21 | false
22 | false
23 | true
24 | 0
25 | 1.0.0.%2a
26 | false
27 | false
28 | true
29 |
30 |
31 |
32 | AnyCPU
33 | true
34 | full
35 | false
36 | bin\Debug\
37 | DEBUG;TRACE
38 | prompt
39 | 4
40 | false
41 |
42 |
43 | AnyCPU
44 | none
45 | true
46 | ..\publish\
47 | TRACE
48 | prompt
49 | 4
50 | false
51 | false
52 |
53 |
54 | 038.ico
55 |
56 |
57 |
58 | ..\packages\log4net.2.0.5\lib\net45-full\log4net.dll
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | Component
80 |
81 |
82 | ProjectInstaller.cs
83 |
84 |
85 | Component
86 |
87 |
88 | ServiceHost.cs
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 | PreserveNewest
105 | Designer
106 |
107 |
108 |
109 | PreserveNewest
110 |
111 |
112 |
113 |
114 | False
115 | Microsoft .NET Framework 4 %28x86 and x64%29
116 | true
117 |
118 |
119 | False
120 | .NET Framework 3.5 SP1 Client Profile
121 | false
122 |
123 |
124 | False
125 | .NET Framework 3.5 SP1
126 | false
127 |
128 |
129 | False
130 | Windows Installer 4.5
131 | true
132 |
133 |
134 |
135 |
136 | ProjectInstaller.cs
137 | Designer
138 |
139 |
140 | ServiceHost.cs
141 | Designer
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 | COPY /Y $(SolutionDir)LICENSE $(TargetDir)
150 | COPY /Y $(SolutionDir)README.md $(TargetDir)
151 | DEL /Q $(TargetDir)*.xml
152 |
153 |
160 |
--------------------------------------------------------------------------------
/ServiceOnset/ServiceOnset.exe.json:
--------------------------------------------------------------------------------
1 | {
2 | "enableLog": true,
3 | "services": [
4 | {
5 | "disable": false,
6 | "name": "Ping-Baidu",
7 | "desc": "Termly PING www.baidu.com",
8 | "command": "ping",
9 | "arguments": "www.baidu.com",
10 | "workingDirectory": "",
11 | "runMode": "interval",
12 | "intervalInSeconds": 30,
13 | "timingExp": "00",
14 | "useShellExecute": false,
15 | "hideWindow": false,
16 | "killExistingProcess": false,
17 | "enableLog": true
18 | }
19 | ]
20 | }
--------------------------------------------------------------------------------
/ServiceOnset/Services/DaemonService.cs:
--------------------------------------------------------------------------------
1 | using ServiceOnset.Config;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Diagnostics;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading;
8 |
9 | namespace ServiceOnset.Services
10 | {
11 | public class DaemonService : ServiceBase
12 | {
13 | #region IDisposable
14 |
15 | private bool disposed = false;
16 | protected override void Dispose(bool disposing)
17 | {
18 | if (!disposed)
19 | {
20 | if (disposing)
21 | {
22 | #region Dispose managed resources
23 |
24 | if (InnerProcess != null)
25 | {
26 | try
27 | {
28 | InnerProcess.Dispose();
29 | }
30 | catch
31 | {
32 | }
33 | finally
34 | {
35 | InnerProcess = null;
36 | }
37 | }
38 |
39 | #endregion
40 | }
41 |
42 | #region Clean up unmanaged resources
43 |
44 | //
45 |
46 | #endregion
47 |
48 | disposed = true;
49 | }
50 |
51 | base.Dispose(disposing);
52 | }
53 |
54 | ~DaemonService()
55 | {
56 | Dispose(false);
57 | }
58 |
59 | #endregion
60 |
61 | protected Process InnerProcess
62 | {
63 | get;
64 | private set;
65 | }
66 |
67 | public DaemonService(IServiceStartInfo startInfo)
68 | : base(startInfo)
69 | {
70 | InnerProcess = CreateProcess();
71 | }
72 |
73 | protected override void ThreadProc()
74 | {
75 | while (IsRunning)
76 | {
77 | try
78 | {
79 | InnerProcess.Start();
80 | EnableOutputRedirection(InnerProcess);
81 |
82 | InnerProcess.WaitForExit();
83 | }
84 | catch (Exception exception)
85 | {
86 | Log.Error("ThreadProc error --->", exception);
87 |
88 | try
89 | {
90 | InnerProcess.Kill();
91 | }
92 | catch { }
93 | }
94 | finally
95 | {
96 | DisableOutputRedirection(InnerProcess);
97 | }
98 |
99 | Thread.Sleep(StartInfo.IntervalInSeconds * 1000);
100 | }
101 | }
102 |
103 | public override void Start()
104 | {
105 | base.Start();
106 | }
107 | public override void Stop()
108 | {
109 | base.Stop();
110 |
111 | try
112 | {
113 | InnerProcess.Kill();
114 | }
115 | catch { }
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/ServiceOnset/Services/IService.cs:
--------------------------------------------------------------------------------
1 | using log4net;
2 | using ServiceOnset.Config;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Diagnostics;
6 | using System.Linq;
7 | using System.Text;
8 |
9 | namespace ServiceOnset.Services
10 | {
11 | public interface IService : IDisposable
12 | {
13 | IServiceStartInfo StartInfo { get; }
14 |
15 | void Start();
16 | void Stop();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/ServiceOnset/Services/Interop.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Runtime.InteropServices;
5 | using System.Security.Principal;
6 | using System.Text;
7 |
8 | namespace ServiceOnset.Services
9 | {
10 | ///
11 | /// Session 0 isolation resolver.
12 | /// (Thank therock)
13 | ///
14 | public static class Interop
15 | {
16 | public static void CreateProcess(string app, string path)
17 | {
18 | bool result;
19 | IntPtr hToken = WindowsIdentity.GetCurrent().Token;
20 | IntPtr hDupedToken = IntPtr.Zero;
21 |
22 | PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
23 | SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
24 | sa.Length = Marshal.SizeOf(sa);
25 | STARTUPINFO si = new STARTUPINFO();
26 | si.cb = Marshal.SizeOf(si);
27 |
28 | int dwSessionID = WTSGetActiveConsoleSessionId();
29 | result = WTSQueryUserToken(dwSessionID, out hToken);
30 | if (!result)
31 | {
32 | throw new Exception("WTSQueryUserToken failed. ERROR: " + Marshal.GetLastWin32Error().ToString());
33 | }
34 |
35 | result = DuplicateTokenEx(
36 | hToken,
37 | GENERIC_ALL_ACCESS,
38 | ref sa,
39 | (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
40 | (int)TOKEN_TYPE.TokenPrimary,
41 | ref hDupedToken
42 | );
43 | if (!result)
44 | {
45 | throw new Exception("DuplicateTokenEx failed. ERROR: " + Marshal.GetLastWin32Error().ToString());
46 | }
47 |
48 | IntPtr lpEnvironment = IntPtr.Zero;
49 | result = CreateEnvironmentBlock(out lpEnvironment, hDupedToken, false);
50 | if (!result)
51 | {
52 | throw new Exception("CreateEnvironmentBlock failed. ERROR: " + Marshal.GetLastWin32Error().ToString());
53 | }
54 |
55 | result = CreateProcessAsUser(
56 | hDupedToken,
57 | app,
58 | string.Empty,
59 | ref sa,
60 | ref sa,
61 | false,
62 | 0,
63 | IntPtr.Zero,
64 | path,
65 | ref si,
66 | ref pi
67 | );
68 | if (!result)
69 | {
70 | throw new Exception("CreateProcessAsUser failed. ERROR: " + Marshal.GetLastWin32Error().ToString());
71 | }
72 |
73 | if (pi.hProcess != IntPtr.Zero)
74 | CloseHandle(pi.hProcess);
75 | if (pi.hThread != IntPtr.Zero)
76 | CloseHandle(pi.hThread);
77 | if (hDupedToken != IntPtr.Zero)
78 | CloseHandle(hDupedToken);
79 | }
80 |
81 | #region Api
82 |
83 | [DllImport("kernel32.dll", SetLastError = true)]
84 | public static extern int WTSGetActiveConsoleSessionId();
85 |
86 | [StructLayout(LayoutKind.Sequential)]
87 | public struct STARTUPINFO
88 | {
89 | public Int32 cb;
90 | public string lpReserved;
91 | public string lpDesktop;
92 | public string lpTitle;
93 | public Int32 dwX;
94 | public Int32 dwY;
95 | public Int32 dwXSize;
96 | public Int32 dwXCountChars;
97 | public Int32 dwYCountChars;
98 | public Int32 dwFillAttribute;
99 | public Int32 dwFlags;
100 | public Int16 wShowWindow;
101 | public Int16 cbReserved2;
102 | public IntPtr lpReserved2;
103 | public IntPtr hStdInput;
104 | public IntPtr hStdOutput;
105 | public IntPtr hStdError;
106 | }
107 |
108 | [StructLayout(LayoutKind.Sequential)]
109 | public struct PROCESS_INFORMATION
110 | {
111 | public IntPtr hProcess;
112 | public IntPtr hThread;
113 | public Int32 dwProcessID;
114 | public Int32 dwThreadID;
115 | }
116 |
117 | [StructLayout(LayoutKind.Sequential)]
118 | public struct SECURITY_ATTRIBUTES
119 | {
120 | public Int32 Length;
121 | public IntPtr lpSecurityDescriptor;
122 | public bool bInheritHandle;
123 | }
124 |
125 | public enum SECURITY_IMPERSONATION_LEVEL
126 | {
127 | SecurityAnonymous,
128 | SecurityIdentification,
129 | SecurityImpersonation,
130 | SecurityDelegation
131 | }
132 | public enum TOKEN_TYPE
133 | {
134 | TokenPrimary = 1,
135 | TokenImpersonation
136 | }
137 |
138 | public const int GENERIC_ALL_ACCESS = 0x10000000;
139 |
140 | [DllImport("kernel32.dll", SetLastError = true,
141 | CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
142 | public static extern bool CloseHandle(IntPtr handle);
143 |
144 | [DllImport("advapi32.dll", SetLastError = true,
145 | CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
146 | public static extern bool CreateProcessAsUser(
147 | IntPtr hToken,
148 | string lpApplicationName,
149 | string lpCommandLine,
150 | ref SECURITY_ATTRIBUTES lpProcessAttributes,
151 | ref SECURITY_ATTRIBUTES lpThreadAttributes,
152 | bool bInheritHandle,
153 | Int32 dwCreationFlags,
154 | IntPtr lpEnvrionment,
155 | string lpCurrentDirectory,
156 | ref STARTUPINFO lpStartupInfo,
157 | ref PROCESS_INFORMATION lpProcessInformation);
158 |
159 | [DllImport("advapi32.dll", SetLastError = true)]
160 | public static extern bool DuplicateTokenEx(
161 | IntPtr hExistingToken,
162 | Int32 dwDesiredAccess,
163 | ref SECURITY_ATTRIBUTES lpThreadAttributes,
164 | Int32 ImpersonationLevel,
165 | Int32 dwTokenType,
166 | ref IntPtr phNewToken);
167 |
168 | [DllImport("wtsapi32.dll", SetLastError = true)]
169 | public static extern bool WTSQueryUserToken(
170 | Int32 sessionId,
171 | out IntPtr Token);
172 |
173 | [DllImport("userenv.dll", SetLastError = true)]
174 | static extern bool CreateEnvironmentBlock(
175 | out IntPtr lpEnvironment,
176 | IntPtr hToken,
177 | bool bInherit);
178 |
179 | #endregion
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/ServiceOnset/Services/IntervalService.cs:
--------------------------------------------------------------------------------
1 | using ServiceOnset.Config;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Diagnostics;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading;
8 |
9 | namespace ServiceOnset.Services
10 | {
11 | public class IntervalService : ServiceBase
12 | {
13 | #region IDisposable
14 |
15 | private bool disposed = false;
16 | protected override void Dispose(bool disposing)
17 | {
18 | if (!disposed)
19 | {
20 | if (disposing)
21 | {
22 | #region Dispose managed resources
23 |
24 | if (InnerProcess != null)
25 | {
26 | try
27 | {
28 | InnerProcess.Dispose();
29 | }
30 | catch
31 | {
32 | }
33 | finally
34 | {
35 | InnerProcess = null;
36 | }
37 | }
38 |
39 | #endregion
40 | }
41 |
42 | #region Clean up unmanaged resources
43 |
44 | //
45 |
46 | #endregion
47 |
48 | disposed = true;
49 | }
50 |
51 | base.Dispose(disposing);
52 | }
53 |
54 | ~IntervalService()
55 | {
56 | Dispose(false);
57 | }
58 |
59 | #endregion
60 |
61 | protected Process InnerProcess
62 | {
63 | get;
64 | private set;
65 | }
66 |
67 | public IntervalService(IServiceStartInfo startInfo)
68 | : base(startInfo)
69 | {
70 | InnerProcess = CreateProcess();
71 | }
72 |
73 | protected override void ThreadProc()
74 | {
75 | while (IsRunning)
76 | {
77 | try
78 | {
79 | InnerProcess.Start();
80 | EnableOutputRedirection(InnerProcess);
81 |
82 | Thread.Sleep(StartInfo.IntervalInSeconds * 1000);
83 | }
84 | catch (Exception exception)
85 | {
86 | Log.Error("ThreadProc error --->", exception);
87 |
88 | Thread.Sleep(StartInfo.IntervalInSeconds * 1000);
89 | }
90 | finally
91 | {
92 | DisableOutputRedirection(InnerProcess);
93 | try
94 | {
95 | InnerProcess.Kill();
96 | }
97 | catch { }
98 | }
99 | }
100 | }
101 |
102 | public override void Start()
103 | {
104 | base.Start();
105 | }
106 | public override void Stop()
107 | {
108 | base.Stop();
109 |
110 | try
111 | {
112 | InnerProcess.Kill();
113 | }
114 | catch { }
115 | }
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/ServiceOnset/Services/LaunchService.cs:
--------------------------------------------------------------------------------
1 | using ServiceOnset.Config;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Diagnostics;
5 | using System.Linq;
6 | using System.Text;
7 |
8 | namespace ServiceOnset.Services
9 | {
10 | public class LaunchService : ServiceBase
11 | {
12 | #region IDisposable
13 |
14 | private bool disposed = false;
15 | protected override void Dispose(bool disposing)
16 | {
17 | if (!disposed)
18 | {
19 | if (disposing)
20 | {
21 | #region Dispose managed resources
22 |
23 | if (InnerProcess != null)
24 | {
25 | try
26 | {
27 | InnerProcess.Dispose();
28 | }
29 | catch
30 | {
31 | }
32 | finally
33 | {
34 | InnerProcess = null;
35 | }
36 | }
37 |
38 | #endregion
39 | }
40 |
41 | #region Clean up unmanaged resources
42 |
43 | //
44 |
45 | #endregion
46 |
47 | disposed = true;
48 | }
49 |
50 | base.Dispose(disposing);
51 | }
52 |
53 | ~LaunchService()
54 | {
55 | Dispose(false);
56 | }
57 |
58 | #endregion
59 |
60 | protected Process InnerProcess
61 | {
62 | get;
63 | private set;
64 | }
65 |
66 | public LaunchService(IServiceStartInfo startInfo)
67 | : base(startInfo)
68 | {
69 | InnerProcess = CreateProcess();
70 | }
71 |
72 | protected override void ThreadProc()
73 | {
74 | if (IsRunning)
75 | {
76 | try
77 | {
78 | InnerProcess.Start();
79 | EnableOutputRedirection(InnerProcess);
80 |
81 | InnerProcess.WaitForExit();
82 | }
83 | catch (Exception exception)
84 | {
85 | Log.Error("ThreadProc error --->", exception);
86 |
87 | try
88 | {
89 | InnerProcess.Kill();
90 | }
91 | catch { }
92 | }
93 | finally
94 | {
95 | DisableOutputRedirection(InnerProcess);
96 | }
97 | }
98 | }
99 |
100 | public override void Start()
101 | {
102 | base.Start();
103 | }
104 | public override void Stop()
105 | {
106 | base.Stop();
107 |
108 | try
109 | {
110 | InnerProcess.Kill();
111 | }
112 | catch { }
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/ServiceOnset/Services/ServiceBase.cs:
--------------------------------------------------------------------------------
1 | using log4net;
2 | using ServiceOnset.Config;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Diagnostics;
6 | using System.IO;
7 | using System.Linq;
8 | using System.Text;
9 | using System.Threading;
10 |
11 | namespace ServiceOnset.Services
12 | {
13 | public abstract class ServiceBase : IService
14 | {
15 | #region IDisposable
16 |
17 | private bool disposed = false;
18 | public void Dispose()
19 | {
20 | Dispose(true);
21 | GC.SuppressFinalize(this);
22 | }
23 | protected virtual void Dispose(bool disposing)
24 | {
25 | if (!disposed)
26 | {
27 | if (disposing)
28 | {
29 | #region Dispose managed resources
30 |
31 | if (IsRunning)
32 | {
33 | IsRunning = false;
34 | }
35 | if (InnerThread != null)
36 | {
37 | try
38 | {
39 | if (InnerThread.IsAlive)
40 | {
41 | InnerThread.Abort();
42 | }
43 | }
44 | catch
45 | {
46 | }
47 | finally
48 | {
49 | InnerThread = null;
50 | }
51 | }
52 | StartInfo = null;
53 | Log = null;
54 |
55 | #endregion
56 | }
57 |
58 | #region Clean up unmanaged resources
59 |
60 | //
61 |
62 | #endregion
63 |
64 | disposed = true;
65 | }
66 | }
67 |
68 | ~ServiceBase()
69 | {
70 | Dispose(false);
71 | }
72 |
73 | #endregion
74 |
75 | public IServiceStartInfo StartInfo
76 | {
77 | get;
78 | private set;
79 | }
80 | protected Logger Log
81 | {
82 | get;
83 | private set;
84 | }
85 | protected Thread InnerThread
86 | {
87 | get;
88 | private set;
89 | }
90 | protected bool IsRunning
91 | {
92 | get;
93 | set;
94 | }
95 |
96 | public ServiceBase(IServiceStartInfo startInfo)
97 | {
98 | StartInfo = startInfo;
99 | Log = new Logger(startInfo.Name, startInfo.EnableLog);
100 |
101 | InnerThread = new Thread(new ThreadStart(ThreadProc));
102 | InnerThread.IsBackground = true;
103 | IsRunning = false;
104 | }
105 |
106 | public virtual void Start()
107 | {
108 | IsRunning = true;
109 | InnerThread.Start();
110 | Log.Info("InnerThread is started");
111 | }
112 | public virtual void Stop()
113 | {
114 | IsRunning = false;
115 | Log.Info("InnerThread is signalled to stop");
116 | }
117 | protected abstract void ThreadProc();
118 |
119 | #region Process helper
120 |
121 | protected Process CreateProcess()
122 | {
123 | if (StartInfo.KillExistingProcess)
124 | {
125 | Log.Info("Try to kill existing process");
126 | Process.GetProcesses().Where(p => TryMatchProcess(p, StartInfo.Command))
127 | .ToList()
128 | .ForEach(p => TryKillProcess(p));
129 | }
130 |
131 | var process = new Process();
132 | process.StartInfo.UseShellExecute = StartInfo.UseShellExecute;
133 | process.StartInfo.FileName = StartInfo.Command;
134 | process.StartInfo.Arguments = StartInfo.Arguments;
135 | process.StartInfo.WorkingDirectory = StartInfo.WorkingDirectory;
136 | Log.Info("InnerProcess is created");
137 |
138 | if (!StartInfo.UseShellExecute)
139 | {
140 | process.StartInfo.RedirectStandardError = true;
141 | process.ErrorDataReceived += new DataReceivedEventHandler((sender, e) =>
142 | {
143 | Log.Error("InnerProcess error: " + e.Data);
144 | });
145 | process.StartInfo.RedirectStandardOutput = true;
146 | process.OutputDataReceived += new DataReceivedEventHandler((sender, e) =>
147 | {
148 | Log.Info("InnerProcess output: " + e.Data);
149 | });
150 |
151 | if (StartInfo.HideWindow)
152 | {
153 | process.StartInfo.CreateNoWindow = true;
154 | }
155 | }
156 | else
157 | {
158 | if (StartInfo.HideWindow)
159 | {
160 | process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
161 | }
162 | }
163 |
164 | if (string.IsNullOrEmpty(Path.GetExtension(process.StartInfo.FileName)))
165 | {
166 | process.StartInfo.FileName = process.StartInfo.FileName.TrimEnd('.') + ".exe";
167 | }
168 | if (!File.Exists(process.StartInfo.FileName) && Directory.Exists(process.StartInfo.WorkingDirectory))
169 | {
170 | process.StartInfo.FileName = Path.Combine(process.StartInfo.WorkingDirectory, Path.GetFileName(process.StartInfo.FileName));
171 | }
172 |
173 | return process;
174 | }
175 |
176 | protected static void EnableOutputRedirection(Process process)
177 | {
178 | try
179 | {
180 | if (process.StartInfo.RedirectStandardOutput)
181 | {
182 | process.BeginOutputReadLine();
183 | }
184 | if (process.StartInfo.RedirectStandardError)
185 | {
186 | process.BeginErrorReadLine();
187 | }
188 | }
189 | catch { }
190 | }
191 | protected static void DisableOutputRedirection(Process process)
192 | {
193 | try
194 | {
195 | if (process.StartInfo.RedirectStandardError)
196 | {
197 | process.CancelErrorRead();
198 | }
199 | if (process.StartInfo.RedirectStandardOutput)
200 | {
201 | process.CancelOutputRead();
202 | }
203 | }
204 | catch { }
205 | }
206 | protected static bool TryMatchProcess(Process process, string command)
207 | {
208 | try
209 | {
210 | return process.MainModule != null
211 | && process.MainModule.FileName != null
212 | && (Path.GetFileName(process.MainModule.FileName).Equals(Path.GetFileName(command), StringComparison.OrdinalIgnoreCase)
213 | || Path.GetFileNameWithoutExtension(process.MainModule.FileName).Equals(Path.GetFileNameWithoutExtension(command), StringComparison.OrdinalIgnoreCase));
214 | }
215 | catch
216 | {
217 | return false;
218 | }
219 | }
220 | protected static void TryKillProcess(Process process)
221 | {
222 | try
223 | {
224 | process.Kill();
225 | }
226 | catch
227 | {
228 | const string taskkillCommand = "TASKKILL";
229 | const string taskkillArguments = "/PID {0}";
230 | const string ntsdCommand = "ntsd";
231 | const string ntsdArguments = "-c q -p {0}";
232 | try
233 | {
234 | using (Process killer = new Process())
235 | {
236 | if (System.Environment.OSVersion.Version.Major >= 6)
237 | {
238 | killer.StartInfo.FileName = taskkillCommand;
239 | killer.StartInfo.Arguments = string.Format(taskkillArguments, process.Id);
240 | }
241 | else
242 | {
243 | killer.StartInfo.FileName = ntsdCommand;
244 | killer.StartInfo.Arguments = string.Format(ntsdArguments, process.Id);
245 | }
246 | killer.StartInfo.UseShellExecute = true;
247 | killer.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
248 | killer.Start();
249 | killer.WaitForExit();
250 | killer.Close();
251 | }
252 | }
253 | catch { }
254 | }
255 | }
256 |
257 | #endregion
258 | }
259 | }
260 |
--------------------------------------------------------------------------------
/ServiceOnset/Services/ServiceFactory.cs:
--------------------------------------------------------------------------------
1 | using ServiceOnset.Config;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 |
7 | namespace ServiceOnset.Services
8 | {
9 | public class ServiceFactory
10 | {
11 | #region Singleton
12 |
13 | private static ServiceFactory _instance;
14 | private static object _mutex = new object();
15 | public static ServiceFactory Instance
16 | {
17 | get
18 | {
19 | if (_instance == null)
20 | {
21 | lock (_mutex)
22 | {
23 | if (_instance == null)
24 | {
25 | _instance = new ServiceFactory();
26 | }
27 | }
28 | }
29 | return _instance;
30 | }
31 | }
32 | private ServiceFactory()
33 | {
34 | }
35 |
36 | #endregion
37 |
38 | public IService Create(IServiceStartInfo startInfo)
39 | {
40 | switch (startInfo.RunMode)
41 | {
42 | case ServiceRunMode.Daemon:
43 | return new DaemonService(startInfo);
44 | case ServiceRunMode.Launch:
45 | return new LaunchService(startInfo);
46 | case ServiceRunMode.Interval:
47 | return new IntervalService(startInfo);
48 | case ServiceRunMode.Timing:
49 | return new TimingService(startInfo);
50 |
51 | default:
52 | throw new ArgumentOutOfRangeException("runMode");
53 | }
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/ServiceOnset/Services/TimingService.cs:
--------------------------------------------------------------------------------
1 | using ServiceOnset.Config;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Diagnostics;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 |
10 | namespace ServiceOnset.Services
11 | {
12 | public class TimingService : ServiceBase
13 | {
14 | #region IDisposable
15 |
16 | private bool disposed = false;
17 | protected override void Dispose(bool disposing)
18 | {
19 | if (!disposed)
20 | {
21 | if (disposing)
22 | {
23 | #region Dispose managed resources
24 |
25 | if (InnerProcess != null)
26 | {
27 | try
28 | {
29 | InnerProcess.Dispose();
30 | }
31 | catch
32 | {
33 | }
34 | finally
35 | {
36 | InnerProcess = null;
37 | }
38 | }
39 |
40 | #endregion
41 | }
42 |
43 | #region Clean up unmanaged resources
44 |
45 | //
46 |
47 | #endregion
48 |
49 | disposed = true;
50 | }
51 |
52 | base.Dispose(disposing);
53 | }
54 |
55 | ~TimingService()
56 | {
57 | Dispose(false);
58 | }
59 |
60 | #endregion
61 |
62 | protected Process InnerProcess
63 | {
64 | get;
65 | private set;
66 | }
67 |
68 | public TimingService(IServiceStartInfo startInfo)
69 | : base(startInfo)
70 | {
71 | InnerProcess = CreateProcess();
72 | }
73 |
74 | protected override void ThreadProc()
75 | {
76 | const string TimingExpFormat = "MMddHHmm";
77 |
78 | var timingExp = StartInfo.TimingExp; // 00
79 | var nowExpFormat = TimingExpFormat.Substring(Math.Max(0, TimingExpFormat.Length - timingExp.Length), Math.Min(TimingExpFormat.Length, timingExp.Length));
80 |
81 | while (IsRunning)
82 | {
83 | #region Timing strategy
84 |
85 | var nowExp = DateTime.Now.ToString(nowExpFormat);
86 | if (nowExp != timingExp)
87 | {
88 | Thread.Sleep(60000);
89 | continue;
90 | }
91 |
92 | #endregion
93 |
94 | try
95 | {
96 | InnerProcess.Start();
97 | EnableOutputRedirection(InnerProcess);
98 |
99 | Thread.Sleep(60000);
100 | }
101 | catch (Exception exception)
102 | {
103 | Log.Error("ThreadProc error --->", exception);
104 |
105 | Thread.Sleep(60000);
106 | }
107 | finally
108 | {
109 | DisableOutputRedirection(InnerProcess);
110 | try
111 | {
112 | InnerProcess.Kill();
113 | }
114 | catch { }
115 | }
116 | }
117 | }
118 |
119 | public override void Start()
120 | {
121 | base.Start();
122 | }
123 | public override void Stop()
124 | {
125 | base.Stop();
126 |
127 | try
128 | {
129 | InnerProcess.Kill();
130 | }
131 | catch { }
132 | }
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/ServiceOnset/log4net.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
34 |
35 |
--------------------------------------------------------------------------------
/ServiceOnset/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/log4net.2.0.5/.signature.p7s:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeddaZ/ServiceOnset/5ca75494e6bb7438f5e13d375f7c9fc7899034bc/packages/log4net.2.0.5/.signature.p7s
--------------------------------------------------------------------------------
/packages/log4net.2.0.5/lib/net10-full/log4net.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeddaZ/ServiceOnset/5ca75494e6bb7438f5e13d375f7c9fc7899034bc/packages/log4net.2.0.5/lib/net10-full/log4net.dll
--------------------------------------------------------------------------------
/packages/log4net.2.0.5/lib/net11-full/log4net.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeddaZ/ServiceOnset/5ca75494e6bb7438f5e13d375f7c9fc7899034bc/packages/log4net.2.0.5/lib/net11-full/log4net.dll
--------------------------------------------------------------------------------
/packages/log4net.2.0.5/lib/net20-full/log4net.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeddaZ/ServiceOnset/5ca75494e6bb7438f5e13d375f7c9fc7899034bc/packages/log4net.2.0.5/lib/net20-full/log4net.dll
--------------------------------------------------------------------------------
/packages/log4net.2.0.5/lib/net35-client/log4net.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeddaZ/ServiceOnset/5ca75494e6bb7438f5e13d375f7c9fc7899034bc/packages/log4net.2.0.5/lib/net35-client/log4net.dll
--------------------------------------------------------------------------------
/packages/log4net.2.0.5/lib/net35-full/log4net.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeddaZ/ServiceOnset/5ca75494e6bb7438f5e13d375f7c9fc7899034bc/packages/log4net.2.0.5/lib/net35-full/log4net.dll
--------------------------------------------------------------------------------
/packages/log4net.2.0.5/lib/net40-client/log4net.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeddaZ/ServiceOnset/5ca75494e6bb7438f5e13d375f7c9fc7899034bc/packages/log4net.2.0.5/lib/net40-client/log4net.dll
--------------------------------------------------------------------------------
/packages/log4net.2.0.5/lib/net40-full/log4net.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeddaZ/ServiceOnset/5ca75494e6bb7438f5e13d375f7c9fc7899034bc/packages/log4net.2.0.5/lib/net40-full/log4net.dll
--------------------------------------------------------------------------------
/packages/log4net.2.0.5/lib/net45-full/log4net.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeddaZ/ServiceOnset/5ca75494e6bb7438f5e13d375f7c9fc7899034bc/packages/log4net.2.0.5/lib/net45-full/log4net.dll
--------------------------------------------------------------------------------
/packages/log4net.2.0.5/log4net.2.0.5.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeddaZ/ServiceOnset/5ca75494e6bb7438f5e13d375f7c9fc7899034bc/packages/log4net.2.0.5/log4net.2.0.5.nupkg
--------------------------------------------------------------------------------