├── .gitignore
├── LICENSE
├── PingTest
├── .gitignore
├── PingTest
│ ├── App.config
│ ├── CommandLineArgsForm.Designer.cs
│ ├── CommandLineArgsForm.cs
│ ├── CommandLineArgsForm.resx
│ ├── Extensions.cs
│ ├── GraphScalingMethod.cs
│ ├── MainForm.Designer.cs
│ ├── MainForm.cs
│ ├── MainForm.resx
│ ├── OptionsForm.Designer.cs
│ ├── OptionsForm.cs
│ ├── OptionsForm.resx
│ ├── PingGraphControl.Designer.cs
│ ├── PingGraphControl.cs
│ ├── PingGraphControl.resx
│ ├── PingLog.cs
│ ├── PingTracer.csproj
│ ├── PingTracer.csproj.vspscc
│ ├── Program.cs
│ ├── Properties
│ │ ├── AssemblyInfo.cs
│ │ ├── Resources.Designer.cs
│ │ ├── Resources.resx
│ │ ├── Settings.Designer.cs
│ │ └── Settings.settings
│ ├── ScreenCapture.cs
│ ├── SerializableObjectBase.cs
│ ├── Settings.cs
│ ├── StartupOptions.cs
│ ├── TestForm.Designer.cs
│ ├── TestForm.cs
│ ├── TestForm.resx
│ ├── Throttle.cs
│ ├── TraceRoute
│ │ ├── PathChangedEventArgs.cs
│ │ ├── PathTracer.cs
│ │ ├── PingResponseEventArgs.cs
│ │ ├── RouteTracerMethodA.cs
│ │ ├── RouteTracerMethodB.cs
│ │ ├── RouteTracerMethodC.cs
│ │ └── TraceRouteHostResult.cs
│ ├── Tracer
│ │ ├── HostSettings.cs
│ │ └── PingController.cs
│ ├── Tracert.cs
│ ├── TracertEntry.cs
│ ├── Util
│ │ ├── PingInstancePool.cs
│ │ └── SimpleThreadPool.cs
│ ├── WindowParams.cs
│ ├── include
│ │ ├── SmartPingF.dll
│ │ ├── Tracer.Designer.cs
│ │ ├── Tracer.cs
│ │ └── Tracer.resx
│ └── pingtracer.ico
├── PingTracer.sln
└── PingTracer.vssscc
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | bld/
23 | [Bb]in/
24 | [Oo]bj/
25 | [Ll]og/
26 |
27 | # Visual Studio 2015/2017 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # Visual Studio 2017 auto generated files
33 | Generated\ Files/
34 |
35 | # MSTest test Results
36 | [Tt]est[Rr]esult*/
37 | [Bb]uild[Ll]og.*
38 |
39 | # NUNIT
40 | *.VisualState.xml
41 | TestResult.xml
42 |
43 | # Build Results of an ATL Project
44 | [Dd]ebugPS/
45 | [Rr]eleasePS/
46 | dlldata.c
47 |
48 | # Benchmark Results
49 | BenchmarkDotNet.Artifacts/
50 |
51 | # .NET Core
52 | project.lock.json
53 | project.fragment.lock.json
54 | artifacts/
55 | **/Properties/launchSettings.json
56 |
57 | # StyleCop
58 | StyleCopReport.xml
59 |
60 | # Files built by Visual Studio
61 | *_i.c
62 | *_p.c
63 | *_i.h
64 | *.ilk
65 | *.meta
66 | *.obj
67 | *.iobj
68 | *.pch
69 | *.pdb
70 | *.ipdb
71 | *.pgc
72 | *.pgd
73 | *.rsp
74 | *.sbr
75 | *.tlb
76 | *.tli
77 | *.tlh
78 | *.tmp
79 | *.tmp_proj
80 | *.log
81 | *.vspscc
82 | *.vssscc
83 | .builds
84 | *.pidb
85 | *.svclog
86 | *.scc
87 |
88 | # Chutzpah Test files
89 | _Chutzpah*
90 |
91 | # Visual C++ cache files
92 | ipch/
93 | *.aps
94 | *.ncb
95 | *.opendb
96 | *.opensdf
97 | *.sdf
98 | *.cachefile
99 | *.VC.db
100 | *.VC.VC.opendb
101 |
102 | # Visual Studio profiler
103 | *.psess
104 | *.vsp
105 | *.vspx
106 | *.sap
107 |
108 | # Visual Studio Trace Files
109 | *.e2e
110 |
111 | # TFS 2012 Local Workspace
112 | $tf/
113 |
114 | # Guidance Automation Toolkit
115 | *.gpState
116 |
117 | # ReSharper is a .NET coding add-in
118 | _ReSharper*/
119 | *.[Rr]e[Ss]harper
120 | *.DotSettings.user
121 |
122 | # JustCode is a .NET coding add-in
123 | .JustCode
124 |
125 | # TeamCity is a build add-in
126 | _TeamCity*
127 |
128 | # DotCover is a Code Coverage Tool
129 | *.dotCover
130 |
131 | # AxoCover is a Code Coverage Tool
132 | .axoCover/*
133 | !.axoCover/settings.json
134 |
135 | # Visual Studio code coverage results
136 | *.coverage
137 | *.coveragexml
138 |
139 | # NCrunch
140 | _NCrunch_*
141 | .*crunch*.local.xml
142 | nCrunchTemp_*
143 |
144 | # MightyMoose
145 | *.mm.*
146 | AutoTest.Net/
147 |
148 | # Web workbench (sass)
149 | .sass-cache/
150 |
151 | # Installshield output folder
152 | [Ee]xpress/
153 |
154 | # DocProject is a documentation generator add-in
155 | DocProject/buildhelp/
156 | DocProject/Help/*.HxT
157 | DocProject/Help/*.HxC
158 | DocProject/Help/*.hhc
159 | DocProject/Help/*.hhk
160 | DocProject/Help/*.hhp
161 | DocProject/Help/Html2
162 | DocProject/Help/html
163 |
164 | # Click-Once directory
165 | publish/
166 |
167 | # Publish Web Output
168 | *.[Pp]ublish.xml
169 | *.azurePubxml
170 | # Note: Comment the next line if you want to checkin your web deploy settings,
171 | # but database connection strings (with potential passwords) will be unencrypted
172 | *.pubxml
173 | *.publishproj
174 |
175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
176 | # checkin your Azure Web App publish settings, but sensitive information contained
177 | # in these scripts will be unencrypted
178 | PublishScripts/
179 |
180 | # NuGet Packages
181 | *.nupkg
182 | # The packages folder can be ignored because of Package Restore
183 | **/[Pp]ackages/*
184 | # except build/, which is used as an MSBuild target.
185 | !**/[Pp]ackages/build/
186 | # Uncomment if necessary however generally it will be regenerated when needed
187 | #!**/[Pp]ackages/repositories.config
188 | # NuGet v3's project.json files produces more ignorable files
189 | *.nuget.props
190 | *.nuget.targets
191 |
192 | # Microsoft Azure Build Output
193 | csx/
194 | *.build.csdef
195 |
196 | # Microsoft Azure Emulator
197 | ecf/
198 | rcf/
199 |
200 | # Windows Store app package directories and files
201 | AppPackages/
202 | BundleArtifacts/
203 | Package.StoreAssociation.xml
204 | _pkginfo.txt
205 | *.appx
206 |
207 | # Visual Studio cache files
208 | # files ending in .cache can be ignored
209 | *.[Cc]ache
210 | # but keep track of directories ending in .cache
211 | !*.[Cc]ache/
212 |
213 | # Others
214 | ClientBin/
215 | ~$*
216 | *~
217 | *.dbmdl
218 | *.dbproj.schemaview
219 | *.jfm
220 | *.pfx
221 | *.publishsettings
222 | orleans.codegen.cs
223 |
224 | # Including strong name files can present a security risk
225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
226 | #*.snk
227 |
228 | # Since there are multiple workflows, uncomment next line to ignore bower_components
229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
230 | #bower_components/
231 |
232 | # RIA/Silverlight projects
233 | Generated_Code/
234 |
235 | # Backup & report files from converting an old project file
236 | # to a newer Visual Studio version. Backup files are not needed,
237 | # because we have git ;-)
238 | _UpgradeReport_Files/
239 | Backup*/
240 | UpgradeLog*.XML
241 | UpgradeLog*.htm
242 | ServiceFabricBackup/
243 | *.rptproj.bak
244 |
245 | # SQL Server files
246 | *.mdf
247 | *.ldf
248 | *.ndf
249 |
250 | # Business Intelligence projects
251 | *.rdl.data
252 | *.bim.layout
253 | *.bim_*.settings
254 | *.rptproj.rsuser
255 |
256 | # Microsoft Fakes
257 | FakesAssemblies/
258 |
259 | # GhostDoc plugin setting file
260 | *.GhostDoc.xml
261 |
262 | # Node.js Tools for Visual Studio
263 | .ntvs_analysis.dat
264 | node_modules/
265 |
266 | # Visual Studio 6 build log
267 | *.plg
268 |
269 | # Visual Studio 6 workspace options file
270 | *.opt
271 |
272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
273 | *.vbw
274 |
275 | # Visual Studio LightSwitch build output
276 | **/*.HTMLClient/GeneratedArtifacts
277 | **/*.DesktopClient/GeneratedArtifacts
278 | **/*.DesktopClient/ModelManifest.xml
279 | **/*.Server/GeneratedArtifacts
280 | **/*.Server/ModelManifest.xml
281 | _Pvt_Extensions
282 |
283 | # Paket dependency manager
284 | .paket/paket.exe
285 | paket-files/
286 |
287 | # FAKE - F# Make
288 | .fake/
289 |
290 | # JetBrains Rider
291 | .idea/
292 | *.sln.iml
293 |
294 | # CodeRush
295 | .cr/
296 |
297 | # Python Tools for Visual Studio (PTVS)
298 | __pycache__/
299 | *.pyc
300 |
301 | # Cake - Uncomment if you are using it
302 | # tools/**
303 | # !tools/packages.config
304 |
305 | # Tabs Studio
306 | *.tss
307 |
308 | # Telerik's JustMock configuration file
309 | *.jmconfig
310 |
311 | # BizTalk build output
312 | *.btp.cs
313 | *.btm.cs
314 | *.odx.cs
315 | *.xsd.cs
316 |
317 | # OpenCover UI analysis results
318 | OpenCover/
319 |
320 | # Azure Stream Analytics local run output
321 | ASALocalRun/
322 |
323 | # MSBuild Binary and Structured Log
324 | *.binlog
325 |
326 | # NVidia Nsight GPU debugger configuration file
327 | *.nvuser
328 |
329 | # MFractors (Xamarin productivity tool) working folder
330 | .mfractor/
331 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 bp2008
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/PingTest/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | bld/
23 | [Bb]in/
24 | [Oo]bj/
25 | [Ll]og/
26 |
27 | # Visual Studio 2015 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # MSTest test Results
33 | [Tt]est[Rr]esult*/
34 | [Bb]uild[Ll]og.*
35 |
36 | # NUNIT
37 | *.VisualState.xml
38 | TestResult.xml
39 |
40 | # Build Results of an ATL Project
41 | [Dd]ebugPS/
42 | [Rr]eleasePS/
43 | dlldata.c
44 |
45 | # .NET Core
46 | project.lock.json
47 | project.fragment.lock.json
48 | artifacts/
49 | **/Properties/launchSettings.json
50 |
51 | *_i.c
52 | *_p.c
53 | *_i.h
54 | *.ilk
55 | *.meta
56 | *.obj
57 | *.pch
58 | #*.pdb
59 | *.pgc
60 | *.pgd
61 | *.rsp
62 | *.sbr
63 | *.tlb
64 | *.tli
65 | *.tlh
66 | *.tmp
67 | *.tmp_proj
68 | *.log
69 | *.vspscc
70 | *.vssscc
71 | .builds
72 | *.pidb
73 | *.svclog
74 | *.scc
75 |
76 | # Chutzpah Test files
77 | _Chutzpah*
78 |
79 | # Visual C++ cache files
80 | ipch/
81 | *.aps
82 | *.ncb
83 | *.opendb
84 | *.opensdf
85 | *.sdf
86 | *.cachefile
87 | *.VC.db
88 | *.VC.VC.opendb
89 |
90 | # Visual Studio profiler
91 | *.psess
92 | *.vsp
93 | *.vspx
94 | *.sap
95 |
96 | # TFS 2012 Local Workspace
97 | $tf/
98 |
99 | # Guidance Automation Toolkit
100 | *.gpState
101 |
102 | # ReSharper is a .NET coding add-in
103 | _ReSharper*/
104 | *.[Rr]e[Ss]harper
105 | *.DotSettings.user
106 |
107 | # JustCode is a .NET coding add-in
108 | .JustCode
109 |
110 | # TeamCity is a build add-in
111 | _TeamCity*
112 |
113 | # DotCover is a Code Coverage Tool
114 | *.dotCover
115 |
116 | # Visual Studio code coverage results
117 | *.coverage
118 | *.coveragexml
119 |
120 | # NCrunch
121 | _NCrunch_*
122 | .*crunch*.local.xml
123 | nCrunchTemp_*
124 |
125 | # MightyMoose
126 | *.mm.*
127 | AutoTest.Net/
128 |
129 | # Web workbench (sass)
130 | .sass-cache/
131 |
132 | # Installshield output folder
133 | [Ee]xpress/
134 |
135 | # DocProject is a documentation generator add-in
136 | DocProject/buildhelp/
137 | DocProject/Help/*.HxT
138 | DocProject/Help/*.HxC
139 | DocProject/Help/*.hhc
140 | DocProject/Help/*.hhk
141 | DocProject/Help/*.hhp
142 | DocProject/Help/Html2
143 | DocProject/Help/html
144 |
145 | # Click-Once directory
146 | publish/
147 |
148 | # Publish Web Output
149 | *.[Pp]ublish.xml
150 | *.azurePubxml
151 | # TODO: Comment the next line if you want to checkin your web deploy settings
152 | # but database connection strings (with potential passwords) will be unencrypted
153 | *.pubxml
154 | *.publishproj
155 |
156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
157 | # checkin your Azure Web App publish settings, but sensitive information contained
158 | # in these scripts will be unencrypted
159 | PublishScripts/
160 |
161 | # NuGet Packages
162 | *.nupkg
163 | # The packages folder can be ignored because of Package Restore
164 | **/packages/*
165 | # except build/, which is used as an MSBuild target.
166 | !**/packages/build/
167 | # Uncomment if necessary however generally it will be regenerated when needed
168 | #!**/packages/repositories.config
169 | # NuGet v3's project.json files produces more ignorable files
170 | *.nuget.props
171 | *.nuget.targets
172 |
173 | # Microsoft Azure Build Output
174 | csx/
175 | *.build.csdef
176 |
177 | # Microsoft Azure Emulator
178 | ecf/
179 | rcf/
180 |
181 | # Windows Store app package directories and files
182 | AppPackages/
183 | BundleArtifacts/
184 | Package.StoreAssociation.xml
185 | _pkginfo.txt
186 |
187 | # Visual Studio cache files
188 | # files ending in .cache can be ignored
189 | *.[Cc]ache
190 | # but keep track of directories ending in .cache
191 | !*.[Cc]ache/
192 |
193 | # Others
194 | ClientBin/
195 | ~$*
196 | *~
197 | *.dbmdl
198 | *.dbproj.schemaview
199 | *.jfm
200 | *.pfx
201 | *.publishsettings
202 | orleans.codegen.cs
203 |
204 | # Since there are multiple workflows, uncomment next line to ignore bower_components
205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
206 | #bower_components/
207 |
208 | # RIA/Silverlight projects
209 | Generated_Code/
210 |
211 | # Backup & report files from converting an old project file
212 | # to a newer Visual Studio version. Backup files are not needed,
213 | # because we have git ;-)
214 | _UpgradeReport_Files/
215 | Backup*/
216 | UpgradeLog*.XML
217 | UpgradeLog*.htm
218 |
219 | # SQL Server files
220 | *.mdf
221 | *.ldf
222 | *.ndf
223 |
224 | # Business Intelligence projects
225 | *.rdl.data
226 | *.bim.layout
227 | *.bim_*.settings
228 |
229 | # Microsoft Fakes
230 | FakesAssemblies/
231 |
232 | # GhostDoc plugin setting file
233 | *.GhostDoc.xml
234 |
235 | # Node.js Tools for Visual Studio
236 | .ntvs_analysis.dat
237 | node_modules/
238 |
239 | # Typescript v1 declaration files
240 | typings/
241 |
242 | # Visual Studio 6 build log
243 | *.plg
244 |
245 | # Visual Studio 6 workspace options file
246 | *.opt
247 |
248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
249 | *.vbw
250 |
251 | # Visual Studio LightSwitch build output
252 | **/*.HTMLClient/GeneratedArtifacts
253 | **/*.DesktopClient/GeneratedArtifacts
254 | **/*.DesktopClient/ModelManifest.xml
255 | **/*.Server/GeneratedArtifacts
256 | **/*.Server/ModelManifest.xml
257 | _Pvt_Extensions
258 |
259 | # Paket dependency manager
260 | .paket/paket.exe
261 | paket-files/
262 |
263 | # FAKE - F# Make
264 | .fake/
265 |
266 | # JetBrains Rider
267 | .idea/
268 | *.sln.iml
269 |
270 | # CodeRush
271 | .cr/
272 |
273 | # Python Tools for Visual Studio (PTVS)
274 | __pycache__/
275 | *.pyc
276 |
277 | # Cake - Uncomment if you are using it
278 | # tools/**
279 | # !tools/packages.config
280 |
281 | # Telerik's JustMock configuration file
282 | *.jmconfig
283 |
284 | # BizTalk build output
285 | *.btp.cs
286 | *.btm.cs
287 | *.odx.cs
288 | *.xsd.cs
289 |
--------------------------------------------------------------------------------
/PingTest/PingTest/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/PingTest/PingTest/CommandLineArgsForm.Designer.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace PingTracer
3 | {
4 | partial class CommandLineArgsForm
5 | {
6 | ///
7 | /// Required designer variable.
8 | ///
9 | private System.ComponentModel.IContainer components = null;
10 |
11 | ///
12 | /// Clean up any resources being used.
13 | ///
14 | /// true if managed resources should be disposed; otherwise, false.
15 | protected override void Dispose(bool disposing)
16 | {
17 | if (disposing && (components != null))
18 | {
19 | components.Dispose();
20 | }
21 | base.Dispose(disposing);
22 | }
23 |
24 | #region Windows Form Designer generated code
25 |
26 | ///
27 | /// Required method for Designer support - do not modify
28 | /// the contents of this method with the code editor.
29 | ///
30 | private void InitializeComponent()
31 | {
32 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(CommandLineArgsForm));
33 | this.txtDocumentation = new System.Windows.Forms.TextBox();
34 | this.label1 = new System.Windows.Forms.Label();
35 | this.txtArgs = new System.Windows.Forms.TextBox();
36 | this.SuspendLayout();
37 | //
38 | // txtDocumentation
39 | //
40 | this.txtDocumentation.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
41 | | System.Windows.Forms.AnchorStyles.Left)
42 | | System.Windows.Forms.AnchorStyles.Right)));
43 | this.txtDocumentation.BackColor = System.Drawing.SystemColors.Window;
44 | this.txtDocumentation.Font = new System.Drawing.Font("Consolas", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
45 | this.txtDocumentation.Location = new System.Drawing.Point(12, 12);
46 | this.txtDocumentation.Multiline = true;
47 | this.txtDocumentation.Name = "txtDocumentation";
48 | this.txtDocumentation.ReadOnly = true;
49 | this.txtDocumentation.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
50 | this.txtDocumentation.Size = new System.Drawing.Size(664, 331);
51 | this.txtDocumentation.TabIndex = 2;
52 | this.txtDocumentation.Text = resources.GetString("txtDocumentation.Text");
53 | //
54 | // label1
55 | //
56 | this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
57 | this.label1.AutoSize = true;
58 | this.label1.Location = new System.Drawing.Point(12, 346);
59 | this.label1.Name = "label1";
60 | this.label1.Size = new System.Drawing.Size(168, 13);
61 | this.label1.TabIndex = 1;
62 | this.label1.Text = "Arguments to start in current state:";
63 | //
64 | // txtArgs
65 | //
66 | this.txtArgs.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
67 | | System.Windows.Forms.AnchorStyles.Right)));
68 | this.txtArgs.BackColor = System.Drawing.SystemColors.Window;
69 | this.txtArgs.Location = new System.Drawing.Point(12, 362);
70 | this.txtArgs.Name = "txtArgs";
71 | this.txtArgs.ReadOnly = true;
72 | this.txtArgs.Size = new System.Drawing.Size(664, 20);
73 | this.txtArgs.TabIndex = 0;
74 | //
75 | // CommandLineArgsForm
76 | //
77 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
78 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
79 | this.ClientSize = new System.Drawing.Size(688, 394);
80 | this.Controls.Add(this.txtArgs);
81 | this.Controls.Add(this.label1);
82 | this.Controls.Add(this.txtDocumentation);
83 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
84 | this.Name = "CommandLineArgsForm";
85 | this.Text = "CommandLineArgsForm";
86 | this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.CommandLineArgsForm_FormClosing);
87 | this.Load += new System.EventHandler(this.CommandLineArgsForm_Load);
88 | this.ResumeLayout(false);
89 | this.PerformLayout();
90 |
91 | }
92 |
93 | #endregion
94 |
95 | private System.Windows.Forms.TextBox txtDocumentation;
96 | private System.Windows.Forms.Label label1;
97 | private System.Windows.Forms.TextBox txtArgs;
98 | }
99 | }
--------------------------------------------------------------------------------
/PingTest/PingTest/CommandLineArgsForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Data;
5 | using System.Drawing;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 | using System.Windows.Forms;
10 |
11 | namespace PingTracer
12 | {
13 | public partial class CommandLineArgsForm : Form
14 | {
15 | MainForm mainForm;
16 | StartupOptions options = new StartupOptions();
17 | public CommandLineArgsForm(MainForm mainForm)
18 | {
19 | this.mainForm = mainForm;
20 |
21 | InitializeComponent();
22 |
23 | this.SetLocationNearMouse();
24 |
25 | mainForm.Move += SetCommandLineArgs;
26 | mainForm.Resize += SetCommandLineArgs;
27 | mainForm.StartedPinging += SetCommandLineArgs;
28 | mainForm.StoppedPinging += SetCommandLineArgs;
29 | mainForm.SelectedHostChanged += SetCommandLineArgs;
30 | mainForm.MaximizeGraphsChanged += SetCommandLineArgs;
31 |
32 | SetCommandLineArgs();
33 | }
34 |
35 | private void CommandLineArgsForm_Load(object sender, EventArgs e)
36 | {
37 | txtDocumentation.Select(0, 0);
38 | txtArgs.Select(0, 0);
39 | txtArgs.Focus();
40 | txtArgs.SelectAll();
41 | }
42 |
43 | private void SetCommandLineArgs(object sender, EventArgs e)
44 | {
45 | SetCommandLineArgs();
46 | }
47 | int mT => mainForm.settings.osWindowTopMargin;
48 | int mL => mainForm.settings.osWindowLeftMargin;
49 | int mR => mainForm.settings.osWindowRightMargin;
50 | int mB => mainForm.settings.osWindowBottomMargin;
51 | private void SetCommandLineArgs()
52 | {
53 | options.WindowLocation = new WindowParams(mainForm.Location.X + mL,
54 | mainForm.Location.Y + mT,
55 | mainForm.Size.Width - (mL + mR),
56 | mainForm.Size.Height - (mT + mB));
57 |
58 | options.StartupHostName = mainForm.txtDisplayName.Text;
59 | if (string.IsNullOrWhiteSpace(options.StartupHostName))
60 | options.StartupHostName = mainForm.txtHost.Text;
61 | if (string.IsNullOrWhiteSpace(options.StartupHostName))
62 | options.StartupHostName = null;
63 |
64 | options.StartPinging = mainForm.isRunning;
65 |
66 | options.MaximizeGraphs = mainForm.graphsMaximized;
67 |
68 | options.PreferIPv6 = mainForm.cbPreferIpv4.Checked ? BoolOverride.False : BoolOverride.True;
69 |
70 | options.TraceRoute = mainForm.cbTraceroute.Checked ? BoolOverride.True : BoolOverride.False;
71 |
72 | txtArgs.Text = options.ToString();
73 | }
74 |
75 | private void CommandLineArgsForm_FormClosing(object sender, FormClosingEventArgs e)
76 | {
77 | mainForm.Move -= SetCommandLineArgs;
78 | mainForm.Resize -= SetCommandLineArgs;
79 | mainForm.StartedPinging -= SetCommandLineArgs;
80 | mainForm.StoppedPinging -= SetCommandLineArgs;
81 | mainForm.SelectedHostChanged -= SetCommandLineArgs;
82 | mainForm.MaximizeGraphsChanged -= SetCommandLineArgs;
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/PingTest/PingTest/CommandLineArgsForm.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 | Arguments:
122 | -h <value>
123 | Load a saved configuration with Display Name or Host field matching <value>.
124 | If a matching configuration is not found, one will be created.
125 | -4
126 | (Use with -h) This indicates the "Prefer IPv4" checkbox must be checked.
127 | -6
128 | (Use with -h) This indicates the "Prefer IPv4" checkbox must be unchecked.
129 | -t0
130 | (Use with -h) This indicates the "Trace Route" checkbox must be unchecked.
131 | -t1
132 | (Use with -h) This indicates the "Trace Route" checkbox must be checked.
133 | -l x,y,w,h
134 | The window will be moved to the specified location and size.
135 | "w" and "h" parameters are optional.
136 | -s
137 | Pinging will begin automatically.
138 | -m
139 | The ping graphs will be maximized.
140 |
141 |
--------------------------------------------------------------------------------
/PingTest/PingTest/Extensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Drawing;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Windows.Forms;
8 |
9 | namespace PingTracer
10 | {
11 | public static class Extensions
12 | {
13 | ///
14 | /// Sets the location of the form to be near the mouse pointer, preferably not directly on top of the mouse pointer, but entirely on-screen if possible.
15 | ///
16 | /// The form.
17 | public static void SetLocationNearMouse(this Form form)
18 | {
19 | int offset = 10;
20 | int x = 0, y = 0;
21 | Point cursor = Cursor.Position;
22 | Screen screen = Screen.FromPoint(cursor);
23 | Rectangle workspace = screen.WorkingArea;
24 | Point centerScreen = new Point(workspace.X + (workspace.Width / 2), workspace.Y + (workspace.Height / 2));
25 |
26 | // Position the form near the cursor, extending away from the cursor toward the center of the screen.
27 | if (cursor.X <= centerScreen.X)
28 | {
29 | if (cursor.Y <= centerScreen.Y)
30 | {
31 | // Upper-left quadrant
32 | x = cursor.X + offset;
33 | y = cursor.Y + offset;
34 | }
35 | else
36 | {
37 | // Lower-left quadrant
38 | x = cursor.X + offset;
39 | y = cursor.Y - offset - form.Height;
40 | }
41 | }
42 | else
43 | {
44 | if (cursor.Y <= centerScreen.Y)
45 | {
46 | // Upper-right quadrant
47 | x = cursor.X - offset - form.Width;
48 | y = cursor.Y + offset;
49 | }
50 | else
51 | {
52 | // Lower-right quadrant
53 | x = cursor.X - offset - form.Width;
54 | y = cursor.Y - offset - form.Height;
55 | }
56 | }
57 |
58 | // Screen bounds check. Keep form entirely within this screen if possible, but ensure that the top left corner is visible if all else fails.
59 | if (x >= workspace.X + (workspace.Width - form.Width))
60 | x = workspace.X + (workspace.Width - form.Width);
61 | if (x < workspace.X)
62 | x = workspace.X;
63 | if (y >= workspace.Y + (workspace.Height - form.Height))
64 | y = workspace.Y + (workspace.Height - form.Height);
65 | if (y < workspace.Y)
66 | y = workspace.Y;
67 |
68 | // Assign location
69 | form.StartPosition = FormStartPosition.Manual;
70 | form.Location = new Point(x, y);
71 | }
72 | ///
73 | /// If the center of the form is not visible on any of the screens, moves the form to the screen whose center is closest to the form in its old position.
74 | ///
75 | /// The form to move.
76 | public static void MoveOnscreenIfOffscreen(this Form form)
77 | {
78 | // Check if the center of the form is visible on any of the screens
79 | bool formIsVisible = false;
80 | Point formCenter = new Point(form.Left + form.Width / 2, form.Top + form.Height / 2);
81 | foreach (Screen screen in Screen.AllScreens)
82 | {
83 | if (screen.WorkingArea.Contains(formCenter))
84 | {
85 | formIsVisible = true;
86 | break;
87 | }
88 | }
89 |
90 | // If the center of the form is not visible, move it to the screen whose center is closest to the form in its old position
91 | if (!formIsVisible)
92 | {
93 | Screen closestScreen = Screen.AllScreens[0];
94 | double minDistance = double.MaxValue;
95 | foreach (Screen screen in Screen.AllScreens)
96 | {
97 | Point screenCenter = new Point(screen.WorkingArea.Left + screen.WorkingArea.Width / 2, screen.WorkingArea.Top + screen.WorkingArea.Height / 2);
98 | double distance = Math.Sqrt(Math.Pow(screenCenter.X - formCenter.X, 2) + Math.Pow(screenCenter.Y - formCenter.Y, 2));
99 | if (distance < minDistance)
100 | {
101 | minDistance = distance;
102 | closestScreen = screen;
103 | }
104 | }
105 | int x = closestScreen.WorkingArea.Left + (closestScreen.WorkingArea.Width - form.Width) / 2;
106 | int y = closestScreen.WorkingArea.Top + (closestScreen.WorkingArea.Height - form.Height) / 2;
107 | form.Location = new Point(x, y);
108 | }
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/PingTest/PingTest/GraphScalingMethod.cs:
--------------------------------------------------------------------------------
1 | namespace PingTracer
2 | {
3 | ///
4 | /// Ping graph scaling methods.
5 | ///
6 | public enum GraphScalingMethod
7 | {
8 | ///
9 | /// The Classic method prefers to treat 1 pixel as 1 millisecond, but can zoom out if needed to show higher response time values correctly.
10 | ///
11 | Classic = 0,
12 | ///
13 | /// The Zoom Unlimited method zooms in or out to closely fit the data but will not zoom out beyond the user's specified limits, possibly causing clipping.
14 | ///
15 | Zoom = 1,
16 | ///
17 | /// The Zoom Unlimited method zooms in or out to closely fit the data.
18 | ///
19 | Zoom_Unlimited = 2,
20 | ///
21 | /// The Fixed method shows exactly the user-specified response time range and does not zoom to fit the data.
22 | ///
23 | Fixed = 3,
24 | }
25 | }
--------------------------------------------------------------------------------
/PingTest/PingTest/OptionsForm.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace PingTracer
2 | {
3 | partial class OptionsForm
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 Windows Form 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.components = new System.ComponentModel.Container();
32 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(OptionsForm));
33 | this.cbLogToFile = new System.Windows.Forms.CheckBox();
34 | this.cbDelayMostRecentPing = new System.Windows.Forms.CheckBox();
35 | this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
36 | this.cbWarnGraphNotLive = new System.Windows.Forms.CheckBox();
37 | this.label1 = new System.Windows.Forms.Label();
38 | this.nudPingResponsesToCache = new System.Windows.Forms.NumericUpDown();
39 | this.label2 = new System.Windows.Forms.Label();
40 | this.cbFastRefreshScrollingGraphs = new System.Windows.Forms.CheckBox();
41 | this.label3 = new System.Windows.Forms.Label();
42 | this.nudGraphScrollMultiplier = new System.Windows.Forms.NumericUpDown();
43 | this.cbShowDateInCorner = new System.Windows.Forms.CheckBox();
44 | this.label4 = new System.Windows.Forms.Label();
45 | this.txtCustomTimeString = new System.Windows.Forms.TextBox();
46 | this.customTimeStringHelp = new System.Windows.Forms.LinkLabel();
47 | this.panel1 = new System.Windows.Forms.Panel();
48 | this.groupBoxFormMargins = new System.Windows.Forms.GroupBox();
49 | this.nudBottomMargin = new System.Windows.Forms.NumericUpDown();
50 | this.nudRightMargin = new System.Windows.Forms.NumericUpDown();
51 | this.nudLeftMargin = new System.Windows.Forms.NumericUpDown();
52 | this.nudTopMargin = new System.Windows.Forms.NumericUpDown();
53 | this.label5 = new System.Windows.Forms.Label();
54 | this.nudPingTimeoutRedLineHeight = new System.Windows.Forms.NumericUpDown();
55 | this.label6 = new System.Windows.Forms.Label();
56 | this.label7 = new System.Windows.Forms.Label();
57 | ((System.ComponentModel.ISupportInitialize)(this.nudPingResponsesToCache)).BeginInit();
58 | ((System.ComponentModel.ISupportInitialize)(this.nudGraphScrollMultiplier)).BeginInit();
59 | this.groupBoxFormMargins.SuspendLayout();
60 | ((System.ComponentModel.ISupportInitialize)(this.nudBottomMargin)).BeginInit();
61 | ((System.ComponentModel.ISupportInitialize)(this.nudRightMargin)).BeginInit();
62 | ((System.ComponentModel.ISupportInitialize)(this.nudLeftMargin)).BeginInit();
63 | ((System.ComponentModel.ISupportInitialize)(this.nudTopMargin)).BeginInit();
64 | ((System.ComponentModel.ISupportInitialize)(this.nudPingTimeoutRedLineHeight)).BeginInit();
65 | this.SuspendLayout();
66 | //
67 | // cbLogToFile
68 | //
69 | this.cbLogToFile.AutoSize = true;
70 | this.cbLogToFile.Checked = true;
71 | this.cbLogToFile.CheckState = System.Windows.Forms.CheckState.Checked;
72 | this.cbLogToFile.Location = new System.Drawing.Point(12, 12);
73 | this.cbLogToFile.Name = "cbLogToFile";
74 | this.cbLogToFile.Size = new System.Drawing.Size(125, 17);
75 | this.cbLogToFile.TabIndex = 1;
76 | this.cbLogToFile.Text = "Log text output to file";
77 | this.toolTip1.SetToolTip(this.cbLogToFile, "Output goes to PingTracer_Output.txt in the current working directory.");
78 | this.cbLogToFile.UseVisualStyleBackColor = true;
79 | this.cbLogToFile.CheckedChanged += new System.EventHandler(this.cbLogToFile_CheckedChanged);
80 | //
81 | // cbDelayMostRecentPing
82 | //
83 | this.cbDelayMostRecentPing.Checked = true;
84 | this.cbDelayMostRecentPing.CheckState = System.Windows.Forms.CheckState.Checked;
85 | this.cbDelayMostRecentPing.Location = new System.Drawing.Point(12, 35);
86 | this.cbDelayMostRecentPing.Name = "cbDelayMostRecentPing";
87 | this.cbDelayMostRecentPing.Size = new System.Drawing.Size(259, 34);
88 | this.cbDelayMostRecentPing.TabIndex = 2;
89 | this.cbDelayMostRecentPing.Text = "Delay ping graphing by one ping interval\r\n(reduces visual flickering)";
90 | this.toolTip1.SetToolTip(this.cbDelayMostRecentPing, "(Checked by default)\r\n\r\nIf unchecked, each wave of pings will appear early, \r\nlik" +
91 | "ely before the ping response has arrived, causing \r\na visual flickering effect w" +
92 | "hen the response arrives.");
93 | this.cbDelayMostRecentPing.UseVisualStyleBackColor = true;
94 | this.cbDelayMostRecentPing.CheckedChanged += new System.EventHandler(this.cbDelayMostRecentPing_CheckedChanged);
95 | //
96 | // toolTip1
97 | //
98 | this.toolTip1.AutomaticDelay = 250;
99 | this.toolTip1.AutoPopDelay = 10000;
100 | this.toolTip1.InitialDelay = 250;
101 | this.toolTip1.ReshowDelay = 50;
102 | //
103 | // cbWarnGraphNotLive
104 | //
105 | this.cbWarnGraphNotLive.AutoSize = true;
106 | this.cbWarnGraphNotLive.Checked = true;
107 | this.cbWarnGraphNotLive.CheckState = System.Windows.Forms.CheckState.Checked;
108 | this.cbWarnGraphNotLive.Location = new System.Drawing.Point(12, 75);
109 | this.cbWarnGraphNotLive.Name = "cbWarnGraphNotLive";
110 | this.cbWarnGraphNotLive.Size = new System.Drawing.Size(290, 17);
111 | this.cbWarnGraphNotLive.TabIndex = 3;
112 | this.cbWarnGraphNotLive.Text = "Warn when graph has been scrolled and is \"NOT LIVE\"";
113 | this.toolTip1.SetToolTip(this.cbWarnGraphNotLive, "(Checked by default)\r\n\r\nIf checked, \"NOT LIVE\" text will appear \r\nwhen you scroll" +
114 | " the graph to the side.");
115 | this.cbWarnGraphNotLive.UseVisualStyleBackColor = true;
116 | this.cbWarnGraphNotLive.CheckedChanged += new System.EventHandler(this.cbWarnGraphNotLive_CheckedChanged);
117 | //
118 | // label1
119 | //
120 | this.label1.AutoSize = true;
121 | this.label1.Location = new System.Drawing.Point(12, 181);
122 | this.label1.Name = "label1";
123 | this.label1.Size = new System.Drawing.Size(293, 13);
124 | this.label1.TabIndex = 11;
125 | this.label1.Text = "Number of ping responses to cache in memory for each host:";
126 | this.toolTip1.SetToolTip(this.label1, resources.GetString("label1.ToolTip"));
127 | //
128 | // nudPingResponsesToCache
129 | //
130 | this.nudPingResponsesToCache.Location = new System.Drawing.Point(12, 200);
131 | this.nudPingResponsesToCache.Maximum = new decimal(new int[] {
132 | 10000000,
133 | 0,
134 | 0,
135 | 0});
136 | this.nudPingResponsesToCache.Minimum = new decimal(new int[] {
137 | 10000,
138 | 0,
139 | 0,
140 | 0});
141 | this.nudPingResponsesToCache.Name = "nudPingResponsesToCache";
142 | this.nudPingResponsesToCache.Size = new System.Drawing.Size(102, 20);
143 | this.nudPingResponsesToCache.TabIndex = 7;
144 | this.toolTip1.SetToolTip(this.nudPingResponsesToCache, resources.GetString("nudPingResponsesToCache.ToolTip"));
145 | this.nudPingResponsesToCache.Value = new decimal(new int[] {
146 | 360000,
147 | 0,
148 | 0,
149 | 0});
150 | this.nudPingResponsesToCache.ValueChanged += new System.EventHandler(this.nudPingResponsesToCache_ValueChanged);
151 | //
152 | // label2
153 | //
154 | this.label2.AutoSize = true;
155 | this.label2.Location = new System.Drawing.Point(120, 202);
156 | this.label2.Name = "label2";
157 | this.label2.Size = new System.Drawing.Size(227, 13);
158 | this.label2.TabIndex = 13;
159 | this.label2.Text = "Takes effect when ping monitoring is restarted.";
160 | this.toolTip1.SetToolTip(this.label2, resources.GetString("label2.ToolTip"));
161 | //
162 | // cbFastRefreshScrollingGraphs
163 | //
164 | this.cbFastRefreshScrollingGraphs.AutoSize = true;
165 | this.cbFastRefreshScrollingGraphs.Checked = true;
166 | this.cbFastRefreshScrollingGraphs.CheckState = System.Windows.Forms.CheckState.Checked;
167 | this.cbFastRefreshScrollingGraphs.Location = new System.Drawing.Point(12, 98);
168 | this.cbFastRefreshScrollingGraphs.Name = "cbFastRefreshScrollingGraphs";
169 | this.cbFastRefreshScrollingGraphs.Size = new System.Drawing.Size(212, 17);
170 | this.cbFastRefreshScrollingGraphs.TabIndex = 4;
171 | this.cbFastRefreshScrollingGraphs.Text = "Accelerate graph redraw when scrolling";
172 | this.toolTip1.SetToolTip(this.cbFastRefreshScrollingGraphs, "(Checked by default)\r\n\r\nIf checked, graphs will update faster while being scrolle" +
173 | "d,\r\nat the cost of increased CPU usage.");
174 | this.cbFastRefreshScrollingGraphs.UseVisualStyleBackColor = true;
175 | this.cbFastRefreshScrollingGraphs.CheckedChanged += new System.EventHandler(this.cbFastRefreshScrollingGraphs_CheckedChanged);
176 | //
177 | // label3
178 | //
179 | this.label3.AutoSize = true;
180 | this.label3.Location = new System.Drawing.Point(9, 150);
181 | this.label3.Name = "label3";
182 | this.label3.Size = new System.Drawing.Size(126, 13);
183 | this.label3.TabIndex = 15;
184 | this.label3.Text = "Graph scrolling multiplier: ";
185 | this.toolTip1.SetToolTip(this.label3, "(Default: 50)\r\n\r\nWhen you click and drag a ping graph horizontally,\r\nit scrolls. " +
186 | " If you increase this value, it will scroll faster.\r\n\r\nIf you set this value to " +
187 | "0, graph scrolling will be disabled.");
188 | //
189 | // nudGraphScrollMultiplier
190 | //
191 | this.nudGraphScrollMultiplier.Location = new System.Drawing.Point(141, 148);
192 | this.nudGraphScrollMultiplier.Maximum = new decimal(new int[] {
193 | 10000,
194 | 0,
195 | 0,
196 | 0});
197 | this.nudGraphScrollMultiplier.Name = "nudGraphScrollMultiplier";
198 | this.nudGraphScrollMultiplier.Size = new System.Drawing.Size(102, 20);
199 | this.nudGraphScrollMultiplier.TabIndex = 6;
200 | this.toolTip1.SetToolTip(this.nudGraphScrollMultiplier, "(Default: 50)\r\n\r\nWhen you click and drag a ping graph horizontally,\r\nit scrolls. " +
201 | " If you increase this value, it will scroll faster.\r\n\r\nIf you set this value to " +
202 | "0, graph scrolling will be disabled.");
203 | this.nudGraphScrollMultiplier.Value = new decimal(new int[] {
204 | 1,
205 | 0,
206 | 0,
207 | 0});
208 | this.nudGraphScrollMultiplier.ValueChanged += new System.EventHandler(this.nudGraphScrollMultiplier_ValueChanged);
209 | //
210 | // cbShowDateInCorner
211 | //
212 | this.cbShowDateInCorner.AutoSize = true;
213 | this.cbShowDateInCorner.Checked = true;
214 | this.cbShowDateInCorner.CheckState = System.Windows.Forms.CheckState.Checked;
215 | this.cbShowDateInCorner.Location = new System.Drawing.Point(12, 121);
216 | this.cbShowDateInCorner.Name = "cbShowDateInCorner";
217 | this.cbShowDateInCorner.Size = new System.Drawing.Size(310, 17);
218 | this.cbShowDateInCorner.TabIndex = 5;
219 | this.cbShowDateInCorner.Text = "Show the current date in the bottom left corner of the graphs";
220 | this.toolTip1.SetToolTip(this.cbShowDateInCorner, "(Checked by default)\r\n\r\nIf checked, the associated date will overlap the bottom\r\n" +
221 | "left corner of the timeline below the graphs.");
222 | this.cbShowDateInCorner.UseVisualStyleBackColor = true;
223 | this.cbShowDateInCorner.CheckedChanged += new System.EventHandler(this.cbShowDateInCorner_CheckedChanged);
224 | //
225 | // label4
226 | //
227 | this.label4.AutoSize = true;
228 | this.label4.Location = new System.Drawing.Point(12, 234);
229 | this.label4.Name = "label4";
230 | this.label4.Size = new System.Drawing.Size(142, 13);
231 | this.label4.TabIndex = 16;
232 | this.label4.Text = "Custom Time String for Logs:";
233 | //
234 | // txtCustomTimeString
235 | //
236 | this.txtCustomTimeString.Location = new System.Drawing.Point(160, 231);
237 | this.txtCustomTimeString.Name = "txtCustomTimeString";
238 | this.txtCustomTimeString.Size = new System.Drawing.Size(147, 20);
239 | this.txtCustomTimeString.TabIndex = 17;
240 | this.txtCustomTimeString.TextChanged += new System.EventHandler(this.txtCustomTimeStringGraphs_TextChanged);
241 | //
242 | // customTimeStringHelp
243 | //
244 | this.customTimeStringHelp.AutoSize = true;
245 | this.customTimeStringHelp.Location = new System.Drawing.Point(313, 234);
246 | this.customTimeStringHelp.Name = "customTimeStringHelp";
247 | this.customTimeStringHelp.Size = new System.Drawing.Size(33, 13);
248 | this.customTimeStringHelp.TabIndex = 18;
249 | this.customTimeStringHelp.TabStop = true;
250 | this.customTimeStringHelp.Text = "(help)";
251 | this.customTimeStringHelp.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.customTimeStringHelp_LinkClicked);
252 | //
253 | // panel1
254 | //
255 | this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
256 | this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
257 | this.panel1.Location = new System.Drawing.Point(228, 42);
258 | this.panel1.Name = "panel1";
259 | this.panel1.Size = new System.Drawing.Size(53, 49);
260 | this.panel1.TabIndex = 19;
261 | //
262 | // groupBoxFormMargins
263 | //
264 | this.groupBoxFormMargins.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
265 | | System.Windows.Forms.AnchorStyles.Left)
266 | | System.Windows.Forms.AnchorStyles.Right)));
267 | this.groupBoxFormMargins.Controls.Add(this.nudBottomMargin);
268 | this.groupBoxFormMargins.Controls.Add(this.nudRightMargin);
269 | this.groupBoxFormMargins.Controls.Add(this.nudLeftMargin);
270 | this.groupBoxFormMargins.Controls.Add(this.nudTopMargin);
271 | this.groupBoxFormMargins.Controls.Add(this.label5);
272 | this.groupBoxFormMargins.Controls.Add(this.panel1);
273 | this.groupBoxFormMargins.Location = new System.Drawing.Point(12, 277);
274 | this.groupBoxFormMargins.Name = "groupBoxFormMargins";
275 | this.groupBoxFormMargins.Size = new System.Drawing.Size(346, 131);
276 | this.groupBoxFormMargins.TabIndex = 20;
277 | this.groupBoxFormMargins.TabStop = false;
278 | this.groupBoxFormMargins.Text = "Window Margins";
279 | //
280 | // nudBottomMargin
281 | //
282 | this.nudBottomMargin.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
283 | this.nudBottomMargin.Location = new System.Drawing.Point(228, 97);
284 | this.nudBottomMargin.Maximum = new decimal(new int[] {
285 | 30,
286 | 0,
287 | 0,
288 | 0});
289 | this.nudBottomMargin.Minimum = new decimal(new int[] {
290 | 30,
291 | 0,
292 | 0,
293 | -2147483648});
294 | this.nudBottomMargin.Name = "nudBottomMargin";
295 | this.nudBottomMargin.Size = new System.Drawing.Size(53, 20);
296 | this.nudBottomMargin.TabIndex = 24;
297 | this.nudBottomMargin.ValueChanged += new System.EventHandler(this.nudBottomMargin_ValueChanged);
298 | //
299 | // nudRightMargin
300 | //
301 | this.nudRightMargin.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
302 | this.nudRightMargin.Location = new System.Drawing.Point(287, 56);
303 | this.nudRightMargin.Maximum = new decimal(new int[] {
304 | 30,
305 | 0,
306 | 0,
307 | 0});
308 | this.nudRightMargin.Minimum = new decimal(new int[] {
309 | 30,
310 | 0,
311 | 0,
312 | -2147483648});
313 | this.nudRightMargin.Name = "nudRightMargin";
314 | this.nudRightMargin.Size = new System.Drawing.Size(53, 20);
315 | this.nudRightMargin.TabIndex = 23;
316 | this.nudRightMargin.ValueChanged += new System.EventHandler(this.nudRightMargin_ValueChanged);
317 | //
318 | // nudLeftMargin
319 | //
320 | this.nudLeftMargin.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
321 | this.nudLeftMargin.Location = new System.Drawing.Point(169, 56);
322 | this.nudLeftMargin.Maximum = new decimal(new int[] {
323 | 30,
324 | 0,
325 | 0,
326 | 0});
327 | this.nudLeftMargin.Minimum = new decimal(new int[] {
328 | 30,
329 | 0,
330 | 0,
331 | -2147483648});
332 | this.nudLeftMargin.Name = "nudLeftMargin";
333 | this.nudLeftMargin.Size = new System.Drawing.Size(53, 20);
334 | this.nudLeftMargin.TabIndex = 22;
335 | this.nudLeftMargin.ValueChanged += new System.EventHandler(this.nudLeftMargin_ValueChanged);
336 | //
337 | // nudTopMargin
338 | //
339 | this.nudTopMargin.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
340 | this.nudTopMargin.Location = new System.Drawing.Point(228, 16);
341 | this.nudTopMargin.Maximum = new decimal(new int[] {
342 | 30,
343 | 0,
344 | 0,
345 | 0});
346 | this.nudTopMargin.Minimum = new decimal(new int[] {
347 | 30,
348 | 0,
349 | 0,
350 | -2147483648});
351 | this.nudTopMargin.Name = "nudTopMargin";
352 | this.nudTopMargin.Size = new System.Drawing.Size(53, 20);
353 | this.nudTopMargin.TabIndex = 21;
354 | this.nudTopMargin.ValueChanged += new System.EventHandler(this.nudTopMargin_ValueChanged);
355 | //
356 | // label5
357 | //
358 | this.label5.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
359 | | System.Windows.Forms.AnchorStyles.Left)
360 | | System.Windows.Forms.AnchorStyles.Right)));
361 | this.label5.Location = new System.Drawing.Point(6, 18);
362 | this.label5.Name = "label5";
363 | this.label5.Size = new System.Drawing.Size(157, 110);
364 | this.label5.TabIndex = 20;
365 | this.label5.Text = "When maximizing the ping graphs, they may appear larger or smaller than the regul" +
366 | "ar program window. To correct this, the following margins will be subtracted fr" +
367 | "om the maximized graph view.";
368 | //
369 | // nudPingTimeoutRedLineHeight
370 | //
371 | this.nudPingTimeoutRedLineHeight.Location = new System.Drawing.Point(160, 257);
372 | this.nudPingTimeoutRedLineHeight.Maximum = new decimal(new int[] {
373 | 10000,
374 | 0,
375 | 0,
376 | 0});
377 | this.nudPingTimeoutRedLineHeight.Minimum = new decimal(new int[] {
378 | 1,
379 | 0,
380 | 0,
381 | 0});
382 | this.nudPingTimeoutRedLineHeight.Name = "nudPingTimeoutRedLineHeight";
383 | this.nudPingTimeoutRedLineHeight.Size = new System.Drawing.Size(64, 20);
384 | this.nudPingTimeoutRedLineHeight.TabIndex = 21;
385 | this.toolTip1.SetToolTip(this.nudPingTimeoutRedLineHeight, "When a ping times out (gets no response), a red line is drawn \r\nup to this many p" +
386 | "ixels tall in the graph. You can reduce this \r\nvalue to shrink the line that is " +
387 | "drawn.");
388 | this.nudPingTimeoutRedLineHeight.Value = new decimal(new int[] {
389 | 10000,
390 | 0,
391 | 0,
392 | 0});
393 | this.nudPingTimeoutRedLineHeight.ValueChanged += new System.EventHandler(this.nudPingTimeoutRedLineHeight_ValueChanged);
394 | //
395 | // label6
396 | //
397 | this.label6.AutoSize = true;
398 | this.label6.Location = new System.Drawing.Point(12, 259);
399 | this.label6.Name = "label6";
400 | this.label6.Size = new System.Drawing.Size(137, 13);
401 | this.label6.TabIndex = 22;
402 | this.label6.Text = "Ping timeout red line height:";
403 | this.toolTip1.SetToolTip(this.label6, "When a ping times out (gets no response), a red line is drawn \r\nup to this many p" +
404 | "ixels tall in the graph. You can reduce this \r\nvalue to shrink the line that is " +
405 | "drawn.");
406 | //
407 | // label7
408 | //
409 | this.label7.AutoSize = true;
410 | this.label7.Location = new System.Drawing.Point(232, 259);
411 | this.label7.Name = "label7";
412 | this.label7.Size = new System.Drawing.Size(39, 13);
413 | this.label7.TabIndex = 23;
414 | this.label7.Text = "(pixels)";
415 | this.toolTip1.SetToolTip(this.label7, "When a ping times out (gets no response), a red line is drawn \r\nup to this many p" +
416 | "ixels tall in the graph. You can reduce this \r\nvalue to shrink the line that is " +
417 | "drawn.");
418 | //
419 | // OptionsForm
420 | //
421 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
422 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
423 | this.ClientSize = new System.Drawing.Size(370, 415);
424 | this.Controls.Add(this.label7);
425 | this.Controls.Add(this.nudPingTimeoutRedLineHeight);
426 | this.Controls.Add(this.label6);
427 | this.Controls.Add(this.groupBoxFormMargins);
428 | this.Controls.Add(this.customTimeStringHelp);
429 | this.Controls.Add(this.txtCustomTimeString);
430 | this.Controls.Add(this.label4);
431 | this.Controls.Add(this.cbShowDateInCorner);
432 | this.Controls.Add(this.nudGraphScrollMultiplier);
433 | this.Controls.Add(this.label3);
434 | this.Controls.Add(this.cbFastRefreshScrollingGraphs);
435 | this.Controls.Add(this.label2);
436 | this.Controls.Add(this.nudPingResponsesToCache);
437 | this.Controls.Add(this.label1);
438 | this.Controls.Add(this.cbWarnGraphNotLive);
439 | this.Controls.Add(this.cbDelayMostRecentPing);
440 | this.Controls.Add(this.cbLogToFile);
441 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
442 | this.Name = "OptionsForm";
443 | this.Text = "Ping Tracer Options";
444 | ((System.ComponentModel.ISupportInitialize)(this.nudPingResponsesToCache)).EndInit();
445 | ((System.ComponentModel.ISupportInitialize)(this.nudGraphScrollMultiplier)).EndInit();
446 | this.groupBoxFormMargins.ResumeLayout(false);
447 | ((System.ComponentModel.ISupportInitialize)(this.nudBottomMargin)).EndInit();
448 | ((System.ComponentModel.ISupportInitialize)(this.nudRightMargin)).EndInit();
449 | ((System.ComponentModel.ISupportInitialize)(this.nudLeftMargin)).EndInit();
450 | ((System.ComponentModel.ISupportInitialize)(this.nudTopMargin)).EndInit();
451 | ((System.ComponentModel.ISupportInitialize)(this.nudPingTimeoutRedLineHeight)).EndInit();
452 | this.ResumeLayout(false);
453 | this.PerformLayout();
454 |
455 | }
456 |
457 | #endregion
458 |
459 | private System.Windows.Forms.CheckBox cbLogToFile;
460 | private System.Windows.Forms.CheckBox cbDelayMostRecentPing;
461 | private System.Windows.Forms.ToolTip toolTip1;
462 | private System.Windows.Forms.CheckBox cbWarnGraphNotLive;
463 | private System.Windows.Forms.Label label1;
464 | private System.Windows.Forms.NumericUpDown nudPingResponsesToCache;
465 | private System.Windows.Forms.Label label2;
466 | private System.Windows.Forms.CheckBox cbFastRefreshScrollingGraphs;
467 | private System.Windows.Forms.Label label3;
468 | private System.Windows.Forms.NumericUpDown nudGraphScrollMultiplier;
469 | private System.Windows.Forms.CheckBox cbShowDateInCorner;
470 | private System.Windows.Forms.Label label4;
471 | private System.Windows.Forms.TextBox txtCustomTimeString;
472 | private System.Windows.Forms.LinkLabel customTimeStringHelp;
473 | private System.Windows.Forms.Panel panel1;
474 | private System.Windows.Forms.GroupBox groupBoxFormMargins;
475 | private System.Windows.Forms.NumericUpDown nudTopMargin;
476 | private System.Windows.Forms.Label label5;
477 | private System.Windows.Forms.NumericUpDown nudBottomMargin;
478 | private System.Windows.Forms.NumericUpDown nudRightMargin;
479 | private System.Windows.Forms.NumericUpDown nudLeftMargin;
480 | private System.Windows.Forms.NumericUpDown nudPingTimeoutRedLineHeight;
481 | private System.Windows.Forms.Label label6;
482 | private System.Windows.Forms.Label label7;
483 | }
484 | }
--------------------------------------------------------------------------------
/PingTest/PingTest/OptionsForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Data;
5 | using System.Diagnostics;
6 | using System.Drawing;
7 | using System.Linq;
8 | using System.Text;
9 | using System.Windows.Forms;
10 |
11 | namespace PingTracer
12 | {
13 | public partial class OptionsForm : Form
14 | {
15 | private MainForm mainForm;
16 |
17 | public OptionsForm()
18 | {
19 | InitializeComponent();
20 | }
21 |
22 | public OptionsForm(MainForm mainForm)
23 | {
24 | this.mainForm = mainForm;
25 | InitializeComponent();
26 | cbLogToFile.Checked = mainForm.settings.logTextOutputToFile;
27 | cbDelayMostRecentPing.Checked = mainForm.settings.delayMostRecentPing;
28 | cbWarnGraphNotLive.Checked = mainForm.settings.warnGraphNotLive;
29 | nudPingResponsesToCache.Value = mainForm.settings.cacheSize;
30 | cbFastRefreshScrollingGraphs.Checked = mainForm.settings.fastRefreshScrollingGraphs;
31 | nudGraphScrollMultiplier.Value = mainForm.settings.graphScrollMultiplier;
32 | cbShowDateInCorner.Checked = mainForm.settings.showDateOnGraphTimeline;
33 | nudPingTimeoutRedLineHeight.Value = mainForm.settings.maxHeightOfPingTimeoutLine;
34 | txtCustomTimeString.Text = mainForm.settings.customTimeStr;
35 | nudTopMargin.Value = mainForm.settings.osWindowTopMargin;
36 | nudLeftMargin.Value = mainForm.settings.osWindowLeftMargin;
37 | nudRightMargin.Value = mainForm.settings.osWindowRightMargin;
38 | nudBottomMargin.Value = mainForm.settings.osWindowBottomMargin;
39 | }
40 |
41 | private void cbLogToFile_CheckedChanged(object sender, EventArgs e)
42 | {
43 | mainForm.settings.logTextOutputToFile = cbLogToFile.Checked;
44 | mainForm.settings.Save();
45 | }
46 |
47 | private void cbDelayMostRecentPing_CheckedChanged(object sender, EventArgs e)
48 | {
49 | mainForm.settings.delayMostRecentPing = cbDelayMostRecentPing.Checked;
50 | mainForm.settings.Save();
51 | }
52 |
53 | private void cbWarnGraphNotLive_CheckedChanged(object sender, EventArgs e)
54 | {
55 | mainForm.settings.warnGraphNotLive = cbWarnGraphNotLive.Checked;
56 | mainForm.settings.Save();
57 | }
58 |
59 | private void nudPingResponsesToCache_ValueChanged(object sender, EventArgs e)
60 | {
61 | mainForm.settings.cacheSize = (int)nudPingResponsesToCache.Value;
62 | mainForm.settings.Save();
63 | }
64 |
65 | private void cbFastRefreshScrollingGraphs_CheckedChanged(object sender, EventArgs e)
66 | {
67 | mainForm.settings.fastRefreshScrollingGraphs = cbFastRefreshScrollingGraphs.Checked;
68 | mainForm.settings.Save();
69 | }
70 |
71 | private void nudGraphScrollMultiplier_ValueChanged(object sender, EventArgs e)
72 | {
73 | mainForm.settings.graphScrollMultiplier = (int)nudGraphScrollMultiplier.Value;
74 | mainForm.settings.Save();
75 | }
76 |
77 | private void cbShowDateInCorner_CheckedChanged(object sender, EventArgs e)
78 | {
79 | mainForm.settings.showDateOnGraphTimeline = cbShowDateInCorner.Checked;
80 | mainForm.settings.Save();
81 | }
82 |
83 | private void customTimeStringHelp_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
84 | {
85 | Process.Start("https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings");
86 | }
87 |
88 | private void txtCustomTimeStringGraphs_TextChanged(object sender, EventArgs e)
89 | {
90 | mainForm.settings.customTimeStr = txtCustomTimeString.Text;
91 | mainForm.settings.Save();
92 | }
93 |
94 | private void nudPingTimeoutRedLineHeight_ValueChanged(object sender, EventArgs e)
95 | {
96 | mainForm.settings.maxHeightOfPingTimeoutLine = (int)nudPingTimeoutRedLineHeight.Value;
97 | mainForm.settings.Save();
98 | }
99 |
100 | private void nudTopMargin_ValueChanged(object sender, EventArgs e)
101 | {
102 | mainForm.settings.osWindowTopMargin = (int)nudTopMargin.Value;
103 | mainForm.settings.Save();
104 | }
105 |
106 | private void nudLeftMargin_ValueChanged(object sender, EventArgs e)
107 | {
108 | mainForm.settings.osWindowLeftMargin = (int)nudLeftMargin.Value;
109 | mainForm.settings.Save();
110 | }
111 |
112 | private void nudRightMargin_ValueChanged(object sender, EventArgs e)
113 | {
114 | mainForm.settings.osWindowRightMargin = (int)nudRightMargin.Value;
115 | mainForm.settings.Save();
116 | }
117 |
118 | private void nudBottomMargin_ValueChanged(object sender, EventArgs e)
119 | {
120 | mainForm.settings.osWindowBottomMargin = (int)nudBottomMargin.Value;
121 | mainForm.settings.Save();
122 | }
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/PingTest/PingTest/PingGraphControl.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace PingTracer
2 | {
3 | partial class PingGraphControl
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.SuspendLayout();
32 | //
33 | // PingGraphControl
34 | //
35 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
36 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
37 | this.Cursor = System.Windows.Forms.Cursors.Default;
38 | this.DoubleBuffered = true;
39 | this.Name = "PingGraphControl";
40 | this.Size = new System.Drawing.Size(610, 110);
41 | this.Paint += new System.Windows.Forms.PaintEventHandler(this.PingGraphControl_Paint);
42 | this.MouseLeave += new System.EventHandler(this.PingGraphControl_MouseLeave);
43 | this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.PingGraphControl_MouseMove);
44 | this.Resize += new System.EventHandler(this.PingGraphControl_Resize);
45 | this.ResumeLayout(false);
46 |
47 | }
48 |
49 | #endregion
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/PingTest/PingTest/PingGraphControl.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Drawing;
5 | using System.Data;
6 | using System.Text;
7 | using System.Windows.Forms;
8 | using System.Threading;
9 | using System.Net.NetworkInformation;
10 | using System.Linq;
11 | using System.Net;
12 |
13 | namespace PingTracer
14 | {
15 | public partial class PingGraphControl : UserControl
16 | {
17 | #region Fields and Properties
18 | private Settings settings = new Settings();
19 |
20 | public static Brush brushSuccess = new SolidBrush(Color.FromArgb(64, 128, 64));
21 | public static Pen penSuccess = new Pen(brushSuccess, 1);
22 | public static Brush brushSuccessBad = new SolidBrush(Color.FromArgb(128, 128, 0));
23 | public static Pen penSuccessBad = new Pen(brushSuccessBad, 1);
24 | public static Brush brushSuccessWorse = new SolidBrush(Color.FromArgb(255, 255, 0));
25 | public static Pen penSuccessWorse = new Pen(brushSuccessWorse, 1);
26 | public static Brush brushFailure = new SolidBrush(Color.FromArgb(255, 0, 0));
27 | public static Pen penFailure = new Pen(brushFailure, 1);
28 | public static Brush brushText = new SolidBrush(Color.FromArgb(255, 255, 255));
29 | public static Color colorBackground = Color.FromArgb(0, 0, 0);
30 | public static Brush brushBackgroundBad = new SolidBrush(Color.FromArgb(35, 35, 0));
31 | public static Brush brushBackgroundWorse = new SolidBrush(Color.FromArgb(40, 0, 0));
32 | public static Brush brushBackgroundTimestamps = new SolidBrush(Color.FromArgb(0, 0, 0));
33 | public static Brush brushTimestampsText = new SolidBrush(Color.FromArgb(200, 200, 200));
34 | public static Pen penTimestampsMark = new Pen(Color.FromArgb(128, 128, 128), 1);
35 | public static Pen penTimestampsBorder = new Pen(Color.FromArgb(128, 128, 128), 1);
36 | public static Font textFont = new Font(FontFamily.GenericSansSerif, 8.25f);
37 | ///
38 | /// Text that is displayed in the upper left corner of the graph.
39 | ///
40 | public string DisplayName = "";
41 | ///
42 | /// A buffer to store information for the most recent pings.
43 | ///
44 | private PingLog[] pings;
45 | ///
46 | /// Gets the number of pings currently cached within the graph control (slow).
47 | ///
48 | public int cachedPings
49 | {
50 | get
51 | {
52 | return pings.Count(x => x != null);
53 | }
54 | }
55 | ///
56 | /// The scroll X offset as a negative number, optionally offset by 1 if the "delay most recent ping" setting is set, in order to delay rendering of the most recent ping.
57 | ///
58 | private int countOffset
59 | {
60 | get
61 | {
62 | return (settings.delayMostRecentPing ? 0 : 1) - scrollXOffset;
63 | }
64 | }
65 | ///
66 | /// The start index to read from the buffer.
67 | ///
68 | private int StartIndex
69 | {
70 | get
71 | {
72 | long currentOffset = Interlocked.Read(ref _nextIndexOffset);
73 | if (currentOffset == -1)
74 | return 0;
75 | return (int)(((currentOffset + countOffset) - DisplayableCount) % pings.Length);
76 | }
77 | }
78 | private int BufferedCount
79 | {
80 | get
81 | {
82 | long currentOffset = Interlocked.Read(ref _nextIndexOffset);
83 | if (currentOffset == -1)
84 | return 0;
85 | else
86 | {
87 | return (int)Math.Min(currentOffset + countOffset, pings.Length);
88 | }
89 | }
90 | }
91 | ///
92 | /// Gets the number of pings we have data for in the current graph viewport. (the number of pings you should interate over, relatiev to StartIndex, when painting)
93 | ///
94 | private int DisplayableCount
95 | {
96 | get
97 | {
98 | return Math.Min(BufferedCount, this.Width);
99 | }
100 | }
101 | ///
102 | /// Counter for tracking the next index in the circular buffer.
103 | ///
104 | private long _nextIndexOffset = -1;
105 | ///
106 | /// The amount of pixels the graph has been scrolled to the left. Scroll position is clamped between 0 and the buffer size.
107 | ///
108 | private int scrollXOffset = 0;
109 | ///
110 | /// Gets or sets the amount of pixels the graph has been scrolled to the left. Scroll position is clamped between 0 and the buffer size.
111 | ///
112 | public int ScrollXOffset
113 | {
114 | get
115 | {
116 | return scrollXOffset;
117 | }
118 | set
119 | {
120 | int v = value;
121 | if (v > pings.Length - this.Width)
122 | v = pings.Length - this.Width;
123 | if (v < 0)
124 | v = 0;
125 | scrollXOffset = v;
126 | if (scrollXOffset == 0)
127 | setLiveAtTime = Environment.TickCount;
128 | }
129 | }
130 | ///
131 | /// Remembers the TickCount (in milliseconds) when the graph was scrolled to the live position, so we can show a message for a short time.
132 | ///
133 | private int setLiveAtTime = 0;
134 | #endregion
135 | public PingGraphControl(Settings settings, IPAddress ipAddress, string hostName, bool reverseDnsLookup)
136 | {
137 | this.settings = settings;
138 | pings = new PingLog[settings.cacheSize];
139 | this.DisplayName = ipAddress.ToString();
140 | if (string.IsNullOrWhiteSpace(hostName))
141 | {
142 | if (reverseDnsLookup)
143 | ThreadPool.QueueUserWorkItem(LookupHostname, ipAddress);
144 | }
145 | else
146 | ConsumeHostName(hostName);
147 | InitializeComponent();
148 | }
149 |
150 | private void LookupHostname(object arg)
151 | {
152 | ConsumeHostName(GetIpHostname((IPAddress)arg));
153 | }
154 |
155 | private string GetIpHostname(IPAddress ip)
156 | {
157 | try
158 | {
159 | return Dns.GetHostEntry(ip).HostName;
160 | }
161 | catch (Exception)
162 | {
163 | }
164 | return string.Empty;
165 | }
166 |
167 | private void ConsumeHostName(string hostName)
168 | {
169 | if (string.IsNullOrWhiteSpace(hostName))
170 | return;
171 | this.DisplayName = hostName + " [" + this.DisplayName + "]";
172 | }
173 |
174 | public void AddPingLog(PingLog pingLog)
175 | {
176 | long newOffset = Interlocked.Increment(ref _nextIndexOffset);
177 | pings[newOffset % pings.Length] = pingLog;
178 | this.Invalidate();
179 | }
180 | public void AddPingLogToSpecificOffset(long offset, PingLog pingLog)
181 | {
182 | pings[offset % pings.Length] = pingLog;
183 | this.Invalidate();
184 | }
185 | ///
186 | /// I'm not sure this function is safe to call.
187 | ///
188 | ///
189 | public void ClearSpecificOffset(long offset)
190 | {
191 | pings[offset % pings.Length] = null;
192 | Interlocked.Exchange(ref _nextIndexOffset, offset);
193 | this.Invalidate();
194 | }
195 | public long ClearNextOffset()
196 | {
197 | long newOffset = Interlocked.Increment(ref _nextIndexOffset);
198 | pings[newOffset % pings.Length] = null;
199 | this.Invalidate();
200 | return newOffset;
201 | }
202 | public void ClearAll()
203 | {
204 | pings = new PingLog[pings.Length];
205 | Interlocked.Exchange(ref _nextIndexOffset, -1);
206 | this.Invalidate();
207 | }
208 |
209 | int timestampsHeight = 13;
210 | public int TimestampsHeight
211 | {
212 | get
213 | {
214 | return timestampsHeight;
215 | }
216 | }
217 | private bool isInvalidatedSync = false;
218 | ///
219 | /// True if the InvalidateSync method has been called and the Paint operation has not yet been performed.
220 | ///
221 | public bool IsInvalidatedSync
222 | {
223 | get
224 | {
225 | return isInvalidatedSync;
226 | }
227 | }
228 | int min = 0, avg = 0, max = 0, last = 0, height = short.MaxValue;
229 | int mouseX = -1;
230 | int mouseY = -1;
231 | ///
232 | /// This number defines the top of the graph. Any response time greater than or equal to this MUST yield a full line on the graph. Any response time less than this MAY yield a less-than-full line on the graph.
233 | ///
234 | int upperLimitDraw = 0;
235 | ///
236 | /// This number defines the bottom of the graph. A ping response time must be greater than this number of milliseconds in order to appear on the graph. For response times of 0ms to appear on the graph, this number must be negative.
237 | ///
238 | int lowerLimitDraw = 0;
239 | ///
240 | /// Gets the number of milliseconds of time covered by the Y axis.
241 | ///
242 | int drawHeight => upperLimitDraw - lowerLimitDraw;
243 | double vScale = 0;
244 | public bool AlwaysShowServerNames = false;
245 | public int Threshold_Bad = 100;
246 | public int Threshold_Worse = 100;
247 | public int upperLimit = 0;
248 | public int lowerLimit = 0;
249 | public bool ShowLastPing = false;
250 | public bool ShowAverage = false;
251 | public bool ShowJitter = false;
252 | public bool ShowMinMax = false;
253 | public bool ShowPacketLoss = false;
254 | public bool ShowTimestamps = true;
255 | public bool DrawLimitText = false;
256 | public GraphScalingMethod ScalingMethod = GraphScalingMethod.Classic;
257 |
258 | private void PingGraphControl_Paint(object sender, PaintEventArgs e)
259 | {
260 | isInvalidatedSync = false;
261 | e.Graphics.Clear(colorBackground);
262 |
263 | bool showTimestampsThisTime = ShowTimestamps;
264 | height = Math.Min(this.Height - (showTimestampsThisTime ? timestampsHeight : 0), short.MaxValue);
265 |
266 | int start = StartIndex;
267 | if (start == -1)
268 | return;
269 |
270 | int count = DisplayableCount;
271 |
272 | max = int.MinValue;
273 | min = int.MaxValue;
274 | int sum = 0;
275 | int successCount = 0;
276 |
277 | // Loop through pings to calculate min, max, and average values
278 | for (int i = 0; i < count; i++)
279 | {
280 | int idx = (start + i) % pings.Length;
281 | PingLog p = pings[idx];
282 | if (p != null && p.result == IPStatus.Success)
283 | {
284 | successCount++;
285 | last = p.pingTime;
286 | sum += last;
287 | max = Math.Max(max, last);
288 | min = Math.Min(min, last);
289 | }
290 | }
291 |
292 | decimal packetLoss = 0;
293 | if (count > 0)
294 | packetLoss = ((count - successCount) / (decimal)count) * 100;
295 |
296 | avg = sum == 0 || successCount == 0 ? 0 : (int)((double)sum / (double)successCount);
297 | if (min == int.MaxValue)
298 | min = 0;
299 | if (max == int.MinValue)
300 | max = 0;
301 |
302 | // Decide how to scale the Y-axis based on configuration and available data.
303 | switch (ScalingMethod)
304 | {
305 | case GraphScalingMethod.Classic:
306 | {
307 | // Zoom out if necessary to fit the response time data, otherwise prefer to draw at an exact ratio of 1px : 1ms.
308 | lowerLimitDraw = lowerLimit;
309 | upperLimitDraw = lowerLimit + height;
310 | if (max > upperLimitDraw)
311 | {
312 | // max value is too high to draw at 1px : 1ms ratio, so zoom out to fit
313 | upperLimitDraw = (int)(max * 1.1);
314 | }
315 | if (upperLimitDraw > upperLimit)
316 | upperLimitDraw = upperLimit;
317 | }
318 | break;
319 | case GraphScalingMethod.Zoom:
320 | {
321 | // Zoom in or out to fit the response time data, but adhere to user-specified limits.
322 | lowerLimitDraw = Math.Max(min - 1, lowerLimit);
323 | upperLimitDraw = Math.Min(max + 1, upperLimit);
324 | }
325 | break;
326 | case GraphScalingMethod.Zoom_Unlimited:
327 | {
328 | // Zoom in or out to fit the response time data.
329 | lowerLimitDraw = min - 1;
330 | upperLimitDraw = max + 1;
331 | }
332 | break;
333 | case GraphScalingMethod.Fixed:
334 | {
335 | // Adhere to user-specified limits, do not dynamically zoom based on the response time data.
336 | lowerLimitDraw = Math.Max(lowerLimit, 0);
337 | upperLimitDraw = Math.Max(upperLimit, 1);
338 | }
339 | break;
340 | }
341 | if (upperLimitDraw <= lowerLimitDraw)
342 | upperLimitDraw = lowerLimitDraw + 1; // Prevent vScale becoming "Infinity" which yields exception.
343 |
344 | vScale = (double)height / (upperLimitDraw - lowerLimitDraw);
345 |
346 | int scaledBadLine = (int)((Threshold_Bad - lowerLimitDraw) * vScale);
347 | int scaledWorseLine = (int)((Threshold_Worse - lowerLimitDraw) * vScale);
348 |
349 | if (scaledWorseLine < height)
350 | e.Graphics.FillRectangle(brushBackgroundWorse, new Rectangle(0, 0, this.Width, height - scaledWorseLine));
351 | if (scaledBadLine < height)
352 | e.Graphics.FillRectangle(brushBackgroundBad, new Rectangle(0, height - scaledWorseLine, this.Width, scaledWorseLine - scaledBadLine));
353 |
354 | if (showTimestampsThisTime)
355 | e.Graphics.DrawLine(penTimestampsBorder, new Point(0, height), new Point(this.Width - 1, height));
356 | // e.Graphics.FillRectangle(brushBackgroundTimestamps, new Rectangle(0, this.Height - timestampsHeight, this.Width, timestampsHeight));
357 |
358 | Point pStart = new Point(this.Width - count, height - 1);
359 | Point pEnd = new Point(this.Width - count, height - 1);
360 | Point pTimestampMarkStart = new Point(this.Width - count, height + 1);
361 | Point pTimestampMarkEnd = new Point(this.Width - count, this.Height - 1);
362 |
363 | int lastStampedMinute = -1;
364 | string timelineOverlayString = "";
365 |
366 | Pen linePen = penSuccess;
367 | Brush lineBrush = brushSuccess;
368 | for (int i = 0; i < count; i++)
369 | {
370 | try
371 | {
372 | int idx = (start + i) % pings.Length;
373 | PingLog p = pings[idx];
374 | if (p == null)
375 | continue;
376 |
377 | bool drawMyLine = false;
378 | if (p.result == IPStatus.Success)
379 | {
380 | if (p.pingTime > lowerLimitDraw)
381 | {
382 | drawMyLine = true;
383 | if (p.pingTime < Threshold_Bad)
384 | {
385 | lineBrush = brushSuccess;
386 | linePen = penSuccess;
387 | }
388 | else if (p.pingTime < Threshold_Worse)
389 | {
390 | lineBrush = brushSuccessBad;
391 | linePen = penSuccessBad;
392 | }
393 | else
394 | {
395 | lineBrush = brushSuccessWorse;
396 | linePen = penSuccessWorse;
397 | }
398 |
399 | pStart.Y = (int)(height - ((p.pingTime - lowerLimitDraw) * vScale));
400 | }
401 | }
402 | else
403 | {
404 | drawMyLine = true;
405 | lineBrush = brushFailure;
406 | linePen = penFailure;
407 | if (height > settings.maxHeightOfPingTimeoutLine)
408 | pStart.Y = height - settings.maxHeightOfPingTimeoutLine;
409 | else
410 | pStart.Y = 0;
411 | }
412 |
413 | if (drawMyLine)
414 | {
415 | if (pStart == pEnd)
416 | e.Graphics.FillRectangle(lineBrush, pStart.X, pStart.Y, 1, 1);
417 | else
418 | e.Graphics.DrawLine(linePen, pStart, pEnd);
419 | }
420 |
421 | // Timestamp drawing logic
422 | if (showTimestampsThisTime)
423 | {
424 | if (lastStampedMinute == -1 || p.startTime.Minute != lastStampedMinute)
425 | {
426 | if (settings.showDateOnGraphTimeline && lastStampedMinute == -1)
427 | timelineOverlayString += p.startTime.ToString("yyyy-M-d ");
428 | if (p.startTime.Second < 2) // Only draw the line if this is close to the actual moment the minute struck.
429 | e.Graphics.DrawLine(penTimestampsMark, pTimestampMarkStart, pTimestampMarkEnd);
430 |
431 | string stamp = p.startTime.ToString("t");
432 | SizeF strSize = e.Graphics.MeasureString(stamp, textFont);
433 | e.Graphics.FillRectangle(brushBackgroundTimestamps, new Rectangle(pTimestampMarkStart.X + 1, pTimestampMarkStart.Y, (int)strSize.Width - 1, (int)timestampsHeight - 1));
434 | e.Graphics.DrawString(stamp, textFont, brushTimestampsText, pTimestampMarkStart.X, pTimestampMarkStart.Y - 1);
435 |
436 | lastStampedMinute = p.startTime.Minute;
437 | }
438 | }
439 | }
440 | finally
441 | {
442 | pStart.X++;
443 | pEnd.X++;
444 | pTimestampMarkStart.X++;
445 | pTimestampMarkEnd.X++;
446 | }
447 | }
448 |
449 | if (count <= 0 && Interlocked.Read(ref _nextIndexOffset) != -1)
450 | timelineOverlayString += "The graph begins " + -count + " lines to the right. ";
451 | else if (scrollXOffset == 0 && Math.Abs(setLiveAtTime - Environment.TickCount) < 1000)
452 | timelineOverlayString += "The graph is now displaying live data. ";
453 |
454 | if (timelineOverlayString.Length > 0)
455 | {
456 | SizeF strSize = e.Graphics.MeasureString(timelineOverlayString, textFont);
457 | e.Graphics.FillRectangle(brushBackgroundTimestamps, new Rectangle(0, pTimestampMarkStart.Y, (int)strSize.Width - 1, (int)timestampsHeight - 1));
458 | e.Graphics.DrawString(timelineOverlayString, textFont, brushTimestampsText, 0, pTimestampMarkStart.Y - 1);
459 | }
460 |
461 | if (DrawLimitText)
462 | {
463 | // Add lower and upper limit labels on the right side
464 | string lowerLimitLabel = lowerLimitDraw.ToString();
465 | string upperLimitLabel = upperLimitDraw.ToString();
466 | SizeF lowerLimitSize = e.Graphics.MeasureString(lowerLimitLabel, textFont);
467 | SizeF upperLimitSize = e.Graphics.MeasureString(upperLimitLabel, textFont);
468 | e.Graphics.DrawString(lowerLimitLabel, textFont, brushText, this.Width - lowerLimitSize.Width, height - lowerLimitSize.Height);
469 | e.Graphics.DrawString(upperLimitLabel, textFont, brushText, this.Width - upperLimitSize.Width, 0);
470 | }
471 |
472 | string statusStr = "";
473 |
474 | if (scrollXOffset != 0 && settings.warnGraphNotLive)
475 | statusStr += "NOT LIVE -" + scrollXOffset + ": ";
476 | if (ShowPacketLoss)
477 | statusStr += packetLoss.ToString("0.00") + "% ";
478 |
479 | List intVals = new List();
480 | if (ShowLastPing)
481 | intVals.Add(last);
482 | if (ShowAverage)
483 | intVals.Add(avg);
484 | if (ShowJitter)
485 | intVals.Add(Math.Abs(max - min));
486 | if (ShowMinMax)
487 | {
488 | intVals.Add(min);
489 | intVals.Add(max);
490 | }
491 |
492 | if (intVals.Count > 0)
493 | statusStr += "[" + string.Join(",", intVals) + "] ";
494 |
495 | string MouseHintText = GetMouseoverHintText();
496 | if (!string.IsNullOrEmpty(MouseHintText))
497 | {
498 | if (!string.IsNullOrEmpty(DisplayName))
499 | statusStr += DisplayName + " ";
500 | statusStr += MouseHintText + " ";
501 | }
502 | else if (AlwaysShowServerNames && !string.IsNullOrEmpty(DisplayName))
503 | statusStr += DisplayName + " ";
504 |
505 | e.Graphics.DrawString(statusStr, textFont, brushText, 1, 1);
506 | //SizeF measuredSize = e.Graphics.MeasureString(statusStr, textFont);
507 | //e.Graphics.DrawString(statusStr, textFont, brushText, (this.Width - measuredSize.Width) - 15, 1);
508 | }
509 |
510 | private void PingGraphControl_Resize(object sender, EventArgs e)
511 | {
512 | this.Invalidate();
513 | }
514 |
515 | private string GetTimestamp(DateTime time)
516 | {
517 | if (!string.IsNullOrWhiteSpace(settings.customTimeStr))
518 | {
519 | try
520 | {
521 | return time.ToString(settings.customTimeStr);
522 | }
523 | catch { }
524 | }
525 | return time.ToString("h:mm:ss tt");
526 | }
527 | ///
528 | /// Invalidates the control, causing a redraw, and marking the IsInvalidatedSync flag = true
529 | ///
530 | public void InvalidateSync()
531 | {
532 | isInvalidatedSync = true;
533 | base.Invalidate();
534 | }
535 | #region Mouseover Hint
536 | private void PingGraphControl_MouseMove(object sender, MouseEventArgs e)
537 | {
538 | mouseX = e.X;
539 | mouseY = e.Y;
540 | this.Invalidate();
541 | }
542 |
543 | private void PingGraphControl_MouseLeave(object sender, EventArgs e)
544 | {
545 | mouseX = mouseY = -1;
546 | this.Invalidate();
547 | }
548 | public string GetMouseoverHintText()
549 | {
550 | if (mouseX < 0 || mouseY < 0)
551 | return "";
552 | int mouseMs = GetScaledHeightValue(height - mouseY);
553 | try
554 | {
555 | int offset = mouseX - this.Width;
556 | int start = StartIndex + DisplayableCount;
557 | int i = (start + offset) % pings.Length;
558 | if (offset <= -pings.Length)
559 | return "Out of bounds, Mouse ms: " + mouseMs;
560 | else if (i < 0)
561 | return "No Data Yet, Mouse ms: " + mouseMs;
562 | PingLog pingLog = pings[i];
563 | if (pingLog == null)
564 | return "Waiting for response, Mouse ms: " + mouseMs;
565 | if (pingLog.result != IPStatus.Success)
566 | return GetTimestamp(pingLog.startTime) + ": " + pingLog.result.ToString() + ", Mouse ms: " + mouseMs;
567 | return GetTimestamp(pingLog.startTime) + ": " + pingLog.pingTime + " ms, Mouse ms: " + mouseMs;
568 | }
569 | catch (Exception)
570 | {
571 | return "Error, Mouse ms: " + mouseMs;
572 | }
573 | }
574 | private int GetScaledHeightValue(int v)
575 | {
576 | if (vScale == 0)
577 | return 0;
578 | double posRelative = (double)v / (double)height;
579 | int posMs = (int)(posRelative * drawHeight);
580 | return posMs + lowerLimitDraw;
581 | }
582 | #endregion
583 | }
584 | }
585 |
--------------------------------------------------------------------------------
/PingTest/PingTest/PingGraphControl.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 |
--------------------------------------------------------------------------------
/PingTest/PingTest/PingLog.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Net.NetworkInformation;
5 |
6 | namespace PingTracer
7 | {
8 | public class PingLog
9 | {
10 | public DateTime startTime;
11 | public short pingTime;
12 | public IPStatus result;
13 | public PingLog(DateTime startTime, short pingTime, IPStatus result)
14 | {
15 | this.startTime = startTime;
16 | this.pingTime = pingTime;
17 | this.result = result;
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/PingTest/PingTest/PingTracer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {854FD525-4946-412E-9B3F-69412F505C30}
8 | WinExe
9 | Properties
10 | PingTracer
11 | PingTracer
12 | v4.6.2
13 | 512
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | AnyCPU
27 | true
28 | full
29 | false
30 | bin\Debug\
31 | DEBUG;TRACE
32 | prompt
33 | 4
34 | false
35 |
36 |
37 | AnyCPU
38 | pdbonly
39 | true
40 | bin\Release\
41 | TRACE
42 | prompt
43 | 4
44 | false
45 |
46 |
47 | pingtracer.ico
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | Form
64 |
65 |
66 | CommandLineArgsForm.cs
67 |
68 |
69 |
70 |
71 | Form
72 |
73 |
74 | Tracer.cs
75 |
76 |
77 | Form
78 |
79 |
80 | MainForm.cs
81 |
82 |
83 | Form
84 |
85 |
86 | OptionsForm.cs
87 |
88 |
89 | UserControl
90 |
91 |
92 | PingGraphControl.cs
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 | Form
108 |
109 |
110 | TestForm.cs
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 | CommandLineArgsForm.cs
124 | Designer
125 |
126 |
127 | Tracer.cs
128 |
129 |
130 | MainForm.cs
131 |
132 |
133 | OptionsForm.cs
134 |
135 |
136 | PingGraphControl.cs
137 |
138 |
139 | ResXFileCodeGenerator
140 | Resources.Designer.cs
141 | Designer
142 |
143 |
144 | True
145 | Resources.resx
146 | True
147 |
148 |
149 | TestForm.cs
150 |
151 |
152 | SettingsSingleFileGenerator
153 | Settings.Designer.cs
154 |
155 |
156 | True
157 | Settings.settings
158 | True
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
177 |
--------------------------------------------------------------------------------
/PingTest/PingTest/PingTracer.csproj.vspscc:
--------------------------------------------------------------------------------
1 | ""
2 | {
3 | "FILE_VERSION" = "9237"
4 | "ENLISTMENT_CHOICE" = "NEVER"
5 | "PROJECT_FILE_RELATIVE_PATH" = ""
6 | "NUMBER_OF_EXCLUDED_FILES" = "0"
7 | "ORIGINAL_PROJECT_FILE_PATH" = ""
8 | "NUMBER_OF_NESTED_PROJECTS" = "0"
9 | "SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
10 | }
11 |
--------------------------------------------------------------------------------
/PingTest/PingTest/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Windows.Forms;
4 |
5 | namespace PingTracer
6 | {
7 | static class Program
8 | {
9 | ///
10 | /// The main entry point for the application.
11 | ///
12 | [STAThread]
13 | static void Main(string[] args)
14 | {
15 | Application.EnableVisualStyles();
16 | Application.SetCompatibleTextRenderingDefault(false);
17 | Application.Run(new MainForm(args));
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/PingTest/PingTest/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("PingTracer")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("PingTracer")]
13 | [assembly: AssemblyCopyright("Copyright © 2024")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("9355c82d-5ce5-4cfd-a921-b1343b4c6850")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.19.1.0")]
36 | [assembly: AssemblyFileVersion("1.19.1.0")]
37 |
--------------------------------------------------------------------------------
/PingTest/PingTest/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace PingTracer.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PingTracer.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/PingTest/PingTest/Properties/Resources.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 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/PingTest/PingTest/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace PingTracer.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.0.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/PingTest/PingTest/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/PingTest/PingTest/ScreenCapture.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 | using System.Drawing;
4 | using System.Drawing.Imaging;
5 |
6 | namespace PingTracer
7 | {
8 | ///
9 | /// Provides functions to capture the entire screen, or a particular window, and save it to a file.
10 | ///
11 | /// FROM http://www.developerfusion.com/code/4630/capture-a-screen-shot/
12 | ///
13 | public class ScreenCapture
14 | {
15 | ///
16 | /// Creates an Image object containing a screen shot of the entire desktop
17 | ///
18 | ///
19 | public Image CaptureScreen()
20 | {
21 | return CaptureWindow(User32.GetDesktopWindow());
22 | }
23 |
24 | ///
25 | /// Creates an Image object containing a screen shot of a specific window
26 | ///
27 | /// The handle to the window. (In windows forms, this is obtained by the Handle property)
28 | ///
29 | public Image CaptureWindow(IntPtr handle)
30 | {
31 | // get te hDC of the target window
32 | IntPtr hdcSrc = User32.GetWindowDC(handle);
33 | // get the size
34 | User32.RECT windowRect = new User32.RECT();
35 | User32.GetWindowRect(handle, ref windowRect);
36 | int width = windowRect.right - windowRect.left;
37 | int height = windowRect.bottom - windowRect.top;
38 | // create a device context we can copy to
39 | IntPtr hdcDest = GDI32.CreateCompatibleDC(hdcSrc);
40 | // create a bitmap we can copy it to,
41 | // using GetDeviceCaps to get the width/height
42 | IntPtr hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc, width, height);
43 | // select the bitmap object
44 | IntPtr hOld = GDI32.SelectObject(hdcDest, hBitmap);
45 | // bitblt over
46 | GDI32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, GDI32.SRCCOPY);
47 | // restore selection
48 | GDI32.SelectObject(hdcDest, hOld);
49 | // clean up
50 | GDI32.DeleteDC(hdcDest);
51 | User32.ReleaseDC(handle, hdcSrc);
52 |
53 | // get a .NET image object for it
54 | Image img = Image.FromHbitmap(hBitmap);
55 | // free up the Bitmap object
56 | GDI32.DeleteObject(hBitmap);
57 |
58 | return img;
59 | }
60 |
61 | ///
62 | /// Captures a screen shot of a specific window, and saves it to a file
63 | ///
64 | ///
65 | ///
66 | ///
67 | public void CaptureWindowToFile(IntPtr handle, string filename, ImageFormat format)
68 | {
69 | Image img = CaptureWindow(handle);
70 | img.Save(filename, format);
71 | }
72 |
73 | ///
74 | /// Captures a screen shot of the entire desktop, and saves it to a file
75 | ///
76 | ///
77 | ///
78 | public void CaptureScreenToFile(string filename, ImageFormat format)
79 | {
80 | Image img = CaptureScreen();
81 | img.Save(filename, format);
82 | }
83 |
84 | ///
85 | /// Helper class containing Gdi32 API functions
86 | ///
87 | private class GDI32
88 | {
89 |
90 | public const int SRCCOPY = 0x00CC0020; // BitBlt dwRop parameter
91 |
92 | [DllImport("gdi32.dll")]
93 | public static extern bool BitBlt(IntPtr hObject, int nXDest, int nYDest,
94 | int nWidth, int nHeight, IntPtr hObjectSource,
95 | int nXSrc, int nYSrc, int dwRop);
96 | [DllImport("gdi32.dll")]
97 | public static extern IntPtr CreateCompatibleBitmap(IntPtr hDC, int nWidth,
98 | int nHeight);
99 | [DllImport("gdi32.dll")]
100 | public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
101 | [DllImport("gdi32.dll")]
102 | public static extern bool DeleteDC(IntPtr hDC);
103 | [DllImport("gdi32.dll")]
104 | public static extern bool DeleteObject(IntPtr hObject);
105 | [DllImport("gdi32.dll")]
106 | public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
107 | }
108 |
109 | ///
110 | /// Helper class containing User32 API functions
111 | ///
112 | private class User32
113 | {
114 | [StructLayout(LayoutKind.Sequential)]
115 | public struct RECT
116 | {
117 | public int left;
118 | public int top;
119 | public int right;
120 | public int bottom;
121 | }
122 |
123 | [DllImport("user32.dll")]
124 | public static extern IntPtr GetDesktopWindow();
125 | [DllImport("user32.dll")]
126 | public static extern IntPtr GetWindowDC(IntPtr hWnd);
127 | [DllImport("user32.dll")]
128 | public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC);
129 | [DllImport("user32.dll")]
130 | public static extern IntPtr GetWindowRect(IntPtr hWnd, ref RECT rect);
131 |
132 | }
133 |
134 |
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/PingTest/PingTest/SerializableObjectBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Reflection;
7 | using System.Text;
8 | using System.Threading;
9 |
10 | namespace PingTracer
11 | {
12 | ///
13 | /// Any class inheriting from this may be loaded and saved from file easily. Uses .
14 | /// Note that strings stored via this class will have '\r' characters removed by the xml writer.
15 | ///
16 | public abstract class SerializableObjectBase
17 | {
18 | private static ConcurrentDictionary fileLocks = new ConcurrentDictionary();
19 | private static object MakeLockKey(string filePath)
20 | {
21 | return filePath;
22 | }
23 | ///
24 | /// Saves this instance to file. Returns true if successful.
25 | ///
26 | /// Optional file path. If null, the default file path is used.
27 | ///
28 | public virtual bool Save(string filePath = null)
29 | {
30 | int tries = 0;
31 | while (tries++ < 5)
32 | try
33 | {
34 | if (filePath == null)
35 | filePath = GetDefaultFilePath();
36 | object lockObj = fileLocks.GetOrAdd(filePath.ToLower(), MakeLockKey);
37 | lock (lockObj)
38 | {
39 | FileInfo fi = new FileInfo(filePath);
40 | if (!fi.Exists)
41 | {
42 | if (!fi.Directory.Exists)
43 | Directory.CreateDirectory(fi.Directory.FullName);
44 | }
45 | using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read))
46 | SerializeObject(this, fs);
47 | }
48 | return true;
49 | }
50 | catch (ThreadAbortException) { throw; }
51 | catch (Exception ex)
52 | {
53 | if (tries >= 5)
54 | {
55 | }
56 | else
57 | Thread.Sleep(1);
58 | }
59 | return false;
60 | }
61 | ///
62 | /// Loads this instance from file. Returns true if successful. May throw an exception if the file format is invalid.
63 | ///
64 | /// Optional file path. If null, the default file path is used.
65 | ///
66 | public virtual bool Load(string filePath = null)
67 | {
68 | int tries = 0;
69 | while (tries++ < 5)
70 | try
71 | {
72 | Type thistype = this.GetType();
73 | if (filePath == null)
74 | filePath = GetDefaultFilePath();
75 | object lockObj = fileLocks.GetOrAdd(filePath.ToLower(), MakeLockKey);
76 | lock (lockObj)
77 | {
78 | if (!File.Exists(filePath))
79 | return false;
80 | object obj;
81 | using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
82 | obj = DeserializeObject(fs);
83 | if (obj != null)
84 | {
85 | foreach (FieldInfo sourceField in obj.GetType().GetFields())
86 | {
87 | try
88 | {
89 | FieldInfo targetField = thistype.GetField(sourceField.Name);
90 | if (targetField != null && targetField.MemberType == sourceField.MemberType)
91 | targetField.SetValue(this, sourceField.GetValue(obj));
92 | }
93 | catch (ThreadAbortException) { throw; }
94 | catch (Exception) { }
95 | }
96 | if (obj.GetType().GetCustomAttributes(typeof(SerializeProperties), false).FirstOrDefault() != null)
97 | {
98 | foreach (PropertyInfo sourceProperty in obj.GetType().GetProperties())
99 | {
100 | try
101 | {
102 | PropertyInfo targetProperty = thistype.GetProperty(sourceProperty.Name);
103 | if (targetProperty != null && targetProperty.MemberType == sourceProperty.MemberType)
104 | targetProperty.SetValue(this, sourceProperty.GetValue(obj));
105 | }
106 | catch (ThreadAbortException) { throw; }
107 | catch (Exception) { }
108 | }
109 | }
110 | }
111 | }
112 | return true;
113 | }
114 | catch (ThreadAbortException) { throw; }
115 | catch (Exception ex)
116 | {
117 | if (tries >= 5)
118 | {
119 | }
120 | else
121 | Thread.Sleep(1);
122 | }
123 | return false;
124 | }
125 | ///
126 | /// (Thread-)Safely checks if the settings file exists, and returns true if it does.
127 | ///
128 | /// Optional file path. If null, the default file path is used.
129 | public virtual bool FileExists(string filePath = null)
130 | {
131 | if (filePath == null)
132 | filePath = GetDefaultFilePath();
133 | object lockObj = fileLocks.GetOrAdd(filePath.ToLower(), MakeLockKey);
134 | lock (lockObj)
135 | return File.Exists(filePath);
136 | }
137 | ///
138 | /// (Thread-)Safely checks if the settings file exists, and if not, saves the current instance. Returns true if a file was saved.
139 | ///
140 | /// Optional file path. If null, the default file path is used.
141 | public virtual bool SaveIfNoExist(string filePath = null)
142 | {
143 | if (filePath == null)
144 | filePath = GetDefaultFilePath();
145 | object lockObj = fileLocks.GetOrAdd(filePath.ToLower(), MakeLockKey);
146 | lock (lockObj)
147 | {
148 | if (!File.Exists(filePath))
149 | return Save(filePath);
150 | }
151 | return false;
152 | }
153 |
154 | ///
155 | /// Gets the default settings file path, which is typically a relative path (to the current working directory).
156 | ///
157 | ///
158 | public virtual string GetDefaultFilePath()
159 | {
160 | return this.GetType().Name + ".cfg";
161 | }
162 |
163 | ///
164 | /// Writes the object to a FileStream. The default implementation in SerializableObjectBase uses XML.
165 | ///
166 | protected virtual void SerializeObject(object obj, FileStream stream)
167 | {
168 | SerializeObjectXml(obj, stream);
169 | }
170 | ///
171 | /// Reads the object from a FileStream. The default implementation in SerializableObjectBase uses XML.
172 | /// Must return a type inheriting from SerializableObjectBase.
173 | ///
174 | protected virtual SerializableObjectBase DeserializeObject(FileStream stream)
175 | {
176 | return DeserializeObjectXml(stream);
177 | }
178 | ///
179 | /// Writes the object to a FileStream using XML.
180 | ///
181 | protected void SerializeObjectXml(object obj, FileStream stream)
182 | {
183 | System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(this.GetType());
184 | x.Serialize(stream, this);
185 | }
186 | ///
187 | /// Reads the object from a FileStream using XML.
188 | /// Must return a type inheriting from SerializableObjectBase.
189 | ///
190 | protected SerializableObjectBase DeserializeObjectXml(FileStream stream)
191 | {
192 | System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(this.GetType());
193 | object obj = x.Deserialize(stream);
194 | return (SerializableObjectBase)obj;
195 | }
196 | }
197 | ///
198 | /// Annotate the serializable object with this in order to load serialized properties (otherwise only fields are loaded from file).
199 | ///
200 | public class SerializeProperties : Attribute
201 | {
202 | public SerializeProperties() { }
203 | }
204 | }
205 |
--------------------------------------------------------------------------------
/PingTest/PingTest/Settings.cs:
--------------------------------------------------------------------------------
1 | using PingTracer.Tracer;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Diagnostics;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Xml;
9 | using System.Xml.Serialization;
10 |
11 | namespace PingTracer
12 | {
13 | public class Settings : SerializableObjectBase
14 | {
15 | public bool logTextOutputToFile = true;
16 | public bool delayMostRecentPing = true;
17 | public bool warnGraphNotLive = true;
18 | public List hostHistory = new List();
19 | public int cacheSize = 360000;
20 | public bool fastRefreshScrollingGraphs = true;
21 | public int graphScrollMultiplier = 50;
22 | public bool showDateOnGraphTimeline = true;
23 | public string customTimeStr;
24 | public WindowParams lastWindowParams = null;
25 | public int osWindowTopMargin = 0;
26 | public int osWindowLeftMargin = 7;
27 | public int osWindowRightMargin = 7;
28 | public int osWindowBottomMargin = 7;
29 | public int maxHeightOfPingTimeoutLine = 10000;
30 |
31 | public bool Save()
32 | {
33 | lock (hostHistory)
34 | {
35 | return Save(settingsFilePath);
36 | }
37 | }
38 | public bool Load()
39 | {
40 | return Load(settingsFilePath);
41 | }
42 | ///
43 | /// Gets the absolute path to the settings file.
44 | ///
45 | private static string settingsFilePath
46 | {
47 | get
48 | {
49 | return settingsFolderPath + "settings.cfg";
50 | }
51 | }
52 | ///
53 | /// Gets the absolute path to the settings folder.
54 | ///
55 | private static string settingsFolderPath
56 | {
57 | get
58 | {
59 | string path = Environment.CurrentDirectory.TrimEnd('/', '\\') + '/';
60 | if (!File.Exists(path + "settings.cfg"))
61 | {
62 | path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData).TrimEnd('/', '\\') + "/PingTracer/";
63 | }
64 | return path;
65 | }
66 | }
67 | ///
68 | /// In Explorer, opens the folder containing the settings file.
69 | ///
70 | public void OpenSettingsFolder()
71 | {
72 | Process.Start(settingsFolderPath);
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/PingTest/PingTest/StartupOptions.cs:
--------------------------------------------------------------------------------
1 | using PingTracer.Tracer;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Windows.Forms;
7 |
8 | namespace PingTracer
9 | {
10 | ///
11 | /// Specifies how the application will behave upon startup.
12 | ///
13 | public partial class StartupOptions
14 | {
15 | ///
16 | /// If not null, pings will start to the stored configuration with this display name or host field value upon application startup.
17 | ///
18 | public string StartupHostName = null;
19 | ///
20 | /// If true, the StartupHostName argument will prefer to match a stored configuration that is configured to prefer IPv6. If false, then IPv4 will be preferred.
21 | ///
22 | public BoolOverride PreferIPv6 = BoolOverride.Inherit;
23 | ///
24 | /// If not null, the window will be positioned here upon application startup. Width or Height values less than 1 will be ignored.
25 | ///
26 | public WindowParams WindowLocation = null;
27 | ///
28 | /// If true, pings will be started automatically upon application startup.
29 | ///
30 | public bool StartPinging = false;
31 | ///
32 | /// If true, the graphs will be maximized automatically upon application startup.
33 | ///
34 | public bool MaximizeGraphs = false;
35 | ///
36 | /// If true, the StartupHostName argument will prefer to match a stored configuration that is configured with the Trace Route option checked.
37 | ///
38 | public BoolOverride TraceRoute = BoolOverride.Inherit;
39 |
40 | ///
41 | /// Constructs an empty StartupOptions.
42 | ///
43 | public StartupOptions() { }
44 | ///
45 | /// Constructs a StartupOptions from an args string.
46 | ///
47 | ///
48 | public StartupOptions(string[] args)
49 | {
50 | HashSet flagKeysWithNoValue = new HashSet(new string[] { "-s", "-m", "-4", "-6", "-t0", "-t1" });
51 | Dictionary flags = new Dictionary();
52 | string key = null;
53 | for (int i = 0; i < args.Length; i++)
54 | {
55 | string arg = args[i];
56 | if (key != null)
57 | {
58 | flags[key] = arg;
59 | key = null;
60 | }
61 | else if (flagKeysWithNoValue.Contains(arg))
62 | flags[arg] = arg;
63 | else
64 | key = arg;
65 | }
66 | if (flags.TryGetValue("-h", out string startupHostName))
67 | this.StartupHostName = startupHostName;
68 | if (flags.TryGetValue("-l", out string startupLocation))
69 | {
70 | try
71 | {
72 | string[] parts = startupLocation.Split(' ', ',', '.');
73 | int[] ints = parts.Select(int.Parse).ToArray();
74 | if (ints.Length == 2)
75 | this.WindowLocation = new WindowParams(ints[0], ints[1], 0, 0);
76 | else if (ints.Length == 3)
77 | this.WindowLocation = new WindowParams(ints[0], ints[1], ints[2], 0);
78 | else if (ints.Length >= 4)
79 | this.WindowLocation = new WindowParams(ints[0], ints[1], ints[2], ints[3]);
80 | }
81 | catch (Exception ex)
82 | {
83 | MessageBox.Show("Unable to load window startup position: \"" + startupLocation + "\" because of error: " + ex.Message);
84 | }
85 | }
86 | if (flags.ContainsKey("-s"))
87 | this.StartPinging = true;
88 | if (flags.ContainsKey("-m"))
89 | this.MaximizeGraphs = true;
90 | if (flags.ContainsKey("-4"))
91 | this.PreferIPv6 = BoolOverride.False;
92 | if (flags.ContainsKey("-6"))
93 | this.PreferIPv6 = BoolOverride.True;
94 | if (flags.ContainsKey("-t1"))
95 | this.TraceRoute = BoolOverride.True;
96 | if (flags.ContainsKey("-t0"))
97 | this.TraceRoute = BoolOverride.False;
98 | }
99 |
100 | ///
101 | /// Returns the command line text that would produce this StartupOptions.
102 | ///
103 | ///
104 | public override string ToString()
105 | {
106 | StringBuilder sb = new StringBuilder();
107 | string[] args = GetArgs();
108 | for (int i = 0; i < args.Length; i++)
109 | {
110 | sb.Append(EscapeCommandLineArgument(args[i], args[i].Contains(' ')));
111 | if (i + 1 < args.Length)
112 | sb.Append(' ');
113 | }
114 | return sb.ToString();
115 | }
116 |
117 | private string[] GetArgs()
118 | {
119 | List args = new List();
120 | if (StartupHostName != null)
121 | {
122 | args.Add("-h");
123 | args.Add(StartupHostName);
124 | }
125 | if (PreferIPv6 == BoolOverride.False)
126 | args.Add("-4");
127 | if (PreferIPv6 == BoolOverride.True)
128 | args.Add("-6");
129 | if (WindowLocation != null)
130 | {
131 | args.Add("-l");
132 | args.Add(WindowLocation.X + "," + WindowLocation.Y + "," + WindowLocation.W + "," + WindowLocation.H);
133 | }
134 | if (StartPinging)
135 | args.Add("-s");
136 | if (MaximizeGraphs)
137 | args.Add("-m");
138 | if (TraceRoute == BoolOverride.True)
139 | args.Add("-t1");
140 | if (TraceRoute == BoolOverride.False)
141 | args.Add("-t0");
142 | return args.ToArray();
143 | }
144 |
145 | ///
146 | /// Escapes backslashes and double-quotation marks by prepending backslashes.
147 | ///
148 | /// Unescaped string.
149 | /// If true, the return value will be wrapped in double quotes.
150 | /// A string suitable to be used as a command line argument.
151 | private static string EscapeCommandLineArgument(string str, bool wrapInDoubleQuotes = false)
152 | {
153 | string dqWrap = wrapInDoubleQuotes ? "\"" : "";
154 | return dqWrap + str.Replace("\\", "\\\\").Replace("\"", "\\\"") + dqWrap;
155 | }
156 | }
157 | public enum BoolOverride
158 | {
159 | Inherit,
160 | False,
161 | True
162 | }
163 | }
--------------------------------------------------------------------------------
/PingTest/PingTest/TestForm.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace PingTracer
2 | {
3 | partial class TestForm
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 Windows Form 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.panel_Graphs = new System.Windows.Forms.Panel();
32 | this.txtOut = new System.Windows.Forms.TextBox();
33 | this.panel_Graphs.SuspendLayout();
34 | this.SuspendLayout();
35 | //
36 | // panel_Graphs
37 | //
38 | this.panel_Graphs.Controls.Add(this.txtOut);
39 | this.panel_Graphs.Dock = System.Windows.Forms.DockStyle.Fill;
40 | this.panel_Graphs.Location = new System.Drawing.Point(0, 0);
41 | this.panel_Graphs.Name = "panel_Graphs";
42 | this.panel_Graphs.Size = new System.Drawing.Size(800, 450);
43 | this.panel_Graphs.TabIndex = 0;
44 | //
45 | // txtOut
46 | //
47 | this.txtOut.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
48 | | System.Windows.Forms.AnchorStyles.Left)
49 | | System.Windows.Forms.AnchorStyles.Right)));
50 | this.txtOut.Font = new System.Drawing.Font("Consolas", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
51 | this.txtOut.Location = new System.Drawing.Point(3, 3);
52 | this.txtOut.Multiline = true;
53 | this.txtOut.Name = "txtOut";
54 | this.txtOut.Size = new System.Drawing.Size(794, 444);
55 | this.txtOut.TabIndex = 0;
56 | //
57 | // TestForm
58 | //
59 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
60 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
61 | this.ClientSize = new System.Drawing.Size(800, 450);
62 | this.Controls.Add(this.panel_Graphs);
63 | this.Name = "TestForm";
64 | this.Text = "TestForm";
65 | this.Load += new System.EventHandler(this.TestForm_Load);
66 | this.panel_Graphs.ResumeLayout(false);
67 | this.panel_Graphs.PerformLayout();
68 | this.ResumeLayout(false);
69 |
70 | }
71 |
72 | #endregion
73 |
74 | private System.Windows.Forms.Panel panel_Graphs;
75 | private System.Windows.Forms.TextBox txtOut;
76 | }
77 | }
--------------------------------------------------------------------------------
/PingTest/PingTest/TestForm.cs:
--------------------------------------------------------------------------------
1 | using PingTracer.TraceRoute;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.ComponentModel;
5 | using System.Data;
6 | using System.Drawing;
7 | using System.Linq;
8 | using System.Net;
9 | using System.Text;
10 | using System.Windows.Forms;
11 |
12 | namespace PingTracer
13 | {
14 | public partial class TestForm : Form
15 | {
16 | const byte maxTtl = 64;
17 | SimpleThreadPool threadPool = new SimpleThreadPool("Ping Test", maxTtl, maxTtl);
18 | List results = new List();
19 | IPAddress addr;
20 | public TestForm(IPAddress addr)
21 | {
22 | this.addr = addr;
23 | InitializeComponent();
24 | }
25 |
26 | private void TestForm_Load(object sender, EventArgs e)
27 | {
28 | try
29 | {
30 | results = new List();
31 | //RouteTracerMethodA.TraceRoute(null, addr, maxTtl, HandlePingResponse);
32 | //RouteTracerMethodB.TraceRoute(null, addr, maxTtl, HandlePingResponse);
33 | RouteTracerMethodC.TraceRoute(threadPool, null, addr, maxTtl, HandlePingResponse);
34 | }
35 | catch (Exception ex)
36 | {
37 | MessageBox.Show(ex.ToString());
38 | }
39 | }
40 | private void HandlePingResponse(TraceRouteHostResult r)
41 | {
42 | lock (this)
43 | {
44 | results.Add(r);
45 | //WriteLine("Got result " + results.Count);
46 | if (results.Count >= maxTtl)
47 | {
48 | results.Sort((a, b) =>
49 | {
50 | return a.ttl.CompareTo(b.ttl);
51 | });
52 | foreach (TraceRouteHostResult result in results)
53 | {
54 | WriteLine(result.ttl.ToString().PadLeft(2, ' ')
55 | + " [" + result.roundTripTime.ToString().PadLeft(4, ' ') + "ms]: "
56 | + result.replyFrom.ToString() + (result.success ? "" : " (failed)"));
57 | }
58 | }
59 | }
60 | }
61 |
62 | private void WriteLine(string str)
63 | {
64 | if (txtOut.InvokeRequired)
65 | txtOut.Invoke((Action)WriteLine, str);
66 | else
67 | {
68 | txtOut.AppendText(str + Environment.NewLine);
69 | }
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/PingTest/PingTest/TestForm.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 |
--------------------------------------------------------------------------------
/PingTest/PingTest/Throttle.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using Timer = System.Threading.Timer;
5 |
6 | namespace PingTracer
7 | {
8 | ///
9 | /// Allows the creation of "throttled" actions. A throttled action can be called many times, but the underlying action will be called at most once per time interval. If the throttled action is called at a time when the underlying action is on cooldown, the underlying action will be called as soon as the cooldown expires. See also for different behavior.
10 | ///
11 | public static class Throttle
12 | {
13 | ///
14 | /// Creates a thread-safe throttled version of the specified action that, when invoked repeatedly, will only actually call the original action at most once per every [wait] milliseconds.
15 | /// When you invoke the action returned by this method, the underlying action will always complete asynchronously.
16 | ///
17 | /// The action to throttle.
18 | /// The number of milliseconds to wait before calling the action again.
19 | /// If the action throws an exception, the exception will be passed to this handler. If the handler is null, the exception will be swallowed.
20 | /// A new action that is a throttled version of the original action.
21 | public static Action Create(Action action, int wait, Action errorHandler)
22 | {
23 | object syncLock = new object();
24 | Timer timer = null;
25 | bool pendingExecution = false;
26 |
27 | Action throttledAction = null;
28 | throttledAction = () =>
29 | {
30 | lock (syncLock)
31 | {
32 | if (timer != null)
33 | {
34 | // We're on cooldown. Schedule the next invokation.
35 | pendingExecution = true;
36 | }
37 | else
38 | {
39 | pendingExecution = false;
40 | // Not on cooldown. Invoke synchronously.
41 | try
42 | {
43 | Task.Run(action);
44 | }
45 | catch (Exception ex)
46 | {
47 | errorHandler?.Invoke(ex);
48 | }
49 |
50 | // Then start the cooldown.
51 | timer = new Timer(_ =>
52 | {
53 | lock (syncLock)
54 | {
55 | timer.Dispose();
56 | timer = null;
57 | // Cooldown expired. Run the action again if required.
58 | if (pendingExecution)
59 | throttledAction();
60 | }
61 | }, null, wait, Timeout.Infinite);
62 | }
63 |
64 | }
65 | };
66 | return throttledAction;
67 | }
68 | }
69 | }
--------------------------------------------------------------------------------
/PingTest/PingTest/TraceRoute/PathChangedEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace PingTracer.TraceRoute
7 | {
8 | public class PathChangedEventArgs : EventArgs
9 | {
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/PingTest/PingTest/TraceRoute/PathTracer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading;
6 |
7 | namespace PingTracer.TraceRoute
8 | {
9 | ///
10 | /// A class which handles monitoring the network path to a host.
11 | ///
12 | public class PathTracer : IDisposable
13 | {
14 | public TimeSpan pingInterval { get; private set; } = TimeSpan.FromSeconds(1);
15 | public readonly string host;
16 | private Timer timer;
17 | ///
18 | /// An event which is raised when the path to a host has changed.
19 | ///
20 | public event EventHandler PathChanged = delegate { };
21 | ///
22 | /// An event which is raised when a ping response is received.
23 | ///
24 | public event EventHandler PingResponse = delegate { };
25 |
26 | public PathTracer(string host, TimeSpan pingInterval)
27 | {
28 | this.host = host;
29 | this.pingInterval = pingInterval;
30 | timer = new Timer(TimerElapsed, null, TimeSpan.Zero, pingInterval);
31 | }
32 |
33 | private void TimerElapsed(object state)
34 | {
35 | }
36 |
37 | #region IDisposable Support
38 | private bool disposedValue = false; // To detect redundant calls
39 |
40 | protected virtual void Dispose(bool disposing)
41 | {
42 | if (!disposedValue)
43 | {
44 | if (disposing)
45 | {
46 | // dispose managed state (managed objects).
47 | }
48 |
49 | // free unmanaged resources (unmanaged objects) and override a finalizer below.
50 | // set large fields to null.
51 |
52 | disposedValue = true;
53 | }
54 | }
55 |
56 | // override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
57 | // ~PathTracer() {
58 | // // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
59 | // Dispose(false);
60 | // }
61 |
62 | // This code added to correctly implement the disposable pattern.
63 | public void Dispose()
64 | {
65 | // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
66 | Dispose(true);
67 | // uncomment the following line if the finalizer is overridden above.
68 | // GC.SuppressFinalize(this);
69 | }
70 | #endregion
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/PingTest/PingTest/TraceRoute/PingResponseEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace PingTracer.TraceRoute
7 | {
8 | public class PingResponseEventArgs : EventArgs
9 | {
10 | }
11 | }
--------------------------------------------------------------------------------
/PingTest/PingTest/TraceRoute/RouteTracerMethodA.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Net.NetworkInformation;
7 | using System.Text;
8 |
9 | namespace PingTracer.TraceRoute
10 | {
11 | public static class RouteTracerMethodA
12 | {
13 | ///
14 | /// Performs an asynchronous, multi-threaded traceroute operation.
15 | ///
16 | /// An object which should be passed through to the OnHostResult method.
17 | /// The target host to ping.
18 | /// The maximum number of hops to try.
19 | /// Callback method which will be called with the result of each individual ping.
20 | /// Timeout in milliseconds after which the ping should be considered unsuccessful.
21 | public static void TraceRoute(object token, IPAddress Target, byte MaxHops, Action OnHostResult, int PingTimeoutMs = 5000)
22 | {
23 | byte[] buffer = new byte[0];
24 | for (byte ttl = 1; ttl <= MaxHops; ttl++)
25 | {
26 | PingOptions opt = new PingOptions(ttl, true);
27 | Ping ping = PingInstancePool.Get();
28 | ping.PingCompleted += Ping_PingCompleted;
29 | Stopwatch sw = new Stopwatch();
30 | sw.Start();
31 | ping.SendAsync(Target, PingTimeoutMs, buffer, opt, new
32 | {
33 | sw,
34 | token,
35 | ttl,
36 | Target,
37 | MaxHops,
38 | OnHostResult,
39 | PingTimeoutMs
40 | });
41 | }
42 | }
43 |
44 | private static void Ping_PingCompleted(object sender, PingCompletedEventArgs e)
45 | {
46 | dynamic state = e.UserState;
47 | Stopwatch sw = state.sw;
48 | sw.Stop();
49 | object token = state.token;
50 | byte ttl = state.ttl;
51 | IPAddress Target = state.Target;
52 | byte MaxHops = state.MaxHops;
53 | Action OnHostResult = state.OnHostResult;
54 | int PingTimeoutMs = state.PingTimeoutMs;
55 |
56 | bool Success = !e.Cancelled && (e.Reply.Status == IPStatus.Success || e.Reply.Status == IPStatus.TtlExpired);
57 | long RoundTripTime = e.Reply.RoundtripTime;
58 | IPAddress ReplyFrom = Success ? e.Reply.Address : IPAddress.Any;
59 |
60 | ((Ping)sender).PingCompleted -= Ping_PingCompleted;
61 | PingInstancePool.Recycle((Ping)sender);
62 |
63 | TraceRouteHostResult result = new TraceRouteHostResult(token, Success, RoundTripTime, ReplyFrom, ttl, Target, MaxHops, PingTimeoutMs);
64 | OnHostResult(result);
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/PingTest/PingTest/TraceRoute/RouteTracerMethodB.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Net.NetworkInformation;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace PingTracer.TraceRoute
11 | {
12 | public static class RouteTracerMethodB
13 | {
14 | ///
15 | /// Performs an asynchronous, multi-threaded traceroute operation.
16 | ///
17 | /// An object which should be passed through to the OnHostResult method.
18 | /// The target host to ping.
19 | /// The maximum number of hops to try.
20 | /// Callback method which will be called with the result of each individual ping.
21 | /// Timeout in milliseconds after which the ping should be considered unsuccessful.
22 | public static void TraceRoute(object token, IPAddress Target, byte MaxHops, Action OnHostResult, int PingTimeoutMs = 5000)
23 | {
24 | List tasks = new List();
25 | byte[] buffer = new byte[0];
26 | for (byte ttl = 1; ttl <= MaxHops; ttl++)
27 | {
28 | tasks.Add(PingAsync(new
29 | {
30 | token,
31 | ttl,
32 | Target,
33 | MaxHops,
34 | OnHostResult,
35 | PingTimeoutMs,
36 | buffer
37 | }));
38 | }
39 | Task.WaitAll(tasks.ToArray());
40 | }
41 |
42 | private static async Task PingAsync(dynamic state)
43 | {
44 | object token = state.token;
45 | byte ttl = state.ttl;
46 | IPAddress Target = state.Target;
47 | byte MaxHops = state.MaxHops;
48 | Action OnHostResult = state.OnHostResult;
49 | int PingTimeoutMs = state.PingTimeoutMs;
50 | byte[] buffer = state.buffer;
51 |
52 | PingOptions opt = new PingOptions(ttl, true);
53 | Ping ping = PingInstancePool.Get();
54 | Stopwatch sw = new Stopwatch();
55 | sw.Start();
56 | PingReply reply = await ping.SendPingAsync(Target, PingTimeoutMs, buffer, opt).ConfigureAwait(false);
57 | sw.Stop();
58 |
59 | bool Success = reply.Status == IPStatus.Success || reply.Status == IPStatus.TtlExpired;
60 | long RoundTripTime = reply.RoundtripTime;
61 | IPAddress ReplyFrom = Success ? reply.Address : IPAddress.Any;
62 |
63 | PingInstancePool.Recycle(ping);
64 |
65 | TraceRouteHostResult result = new TraceRouteHostResult(token, Success, RoundTripTime, ReplyFrom, ttl, Target, MaxHops, PingTimeoutMs);
66 | OnHostResult(result);
67 | }
68 | private class PingTask
69 | {
70 | public object State;
71 | public Task Task;
72 |
73 | public PingTask(object state, Task task)
74 | {
75 | State = state;
76 | Task = task;
77 | }
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/PingTest/PingTest/TraceRoute/RouteTracerMethodC.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Net.NetworkInformation;
7 | using System.Text;
8 |
9 | namespace PingTracer.TraceRoute
10 | {
11 | public static class RouteTracerMethodC
12 | {
13 | ///
14 | /// Performs an asynchronous, multi-threaded traceroute operation.
15 | ///
16 | /// A thread pool upon which to execute the pings. It should contain as many threads as the value.
17 | /// An object which should be passed through to the OnHostResult method.
18 | /// The target host to ping.
19 | /// The maximum number of hops to try.
20 | /// Callback method which will be called with the result of each individual ping.
21 | /// Timeout in milliseconds after which the ping should be considered unsuccessful.
22 | public static void TraceRoute(SimpleThreadPool threadPool, object token, IPAddress Target, byte MaxHops, Action OnHostResult, int PingTimeoutMs = 5000)
23 | {
24 | byte[] buffer = new byte[0];
25 | for (byte ttl = 1; ttl <= MaxHops; ttl++)
26 | {
27 | object state = new
28 | {
29 | token,
30 | ttl,
31 | Target,
32 | MaxHops,
33 | OnHostResult,
34 | PingTimeoutMs,
35 | buffer
36 | };
37 | threadPool.Enqueue(() =>
38 | {
39 | PingSync(state);
40 | });
41 | }
42 | }
43 |
44 | private static void PingSync(dynamic state)
45 | {
46 | object token = state.token;
47 | byte ttl = state.ttl;
48 | IPAddress Target = state.Target;
49 | byte MaxHops = state.MaxHops;
50 | Action OnHostResult = state.OnHostResult;
51 | int PingTimeoutMs = state.PingTimeoutMs;
52 | byte[] buffer = state.buffer;
53 |
54 | PingOptions opt = new PingOptions(ttl, true);
55 | Ping ping = PingInstancePool.Get();
56 | Stopwatch sw = new Stopwatch();
57 | sw.Start();
58 | PingReply reply = ping.Send(Target, PingTimeoutMs, buffer, opt);
59 | sw.Stop();
60 |
61 | bool Success = reply.Status == IPStatus.Success || reply.Status == IPStatus.TtlExpired;
62 | long RoundTripTime = reply.RoundtripTime * 10000 + sw.ElapsedMilliseconds;
63 | IPAddress ReplyFrom = Success ? reply.Address : IPAddress.Any;
64 |
65 | PingInstancePool.Recycle(ping);
66 |
67 | TraceRouteHostResult result = new TraceRouteHostResult(token, Success, RoundTripTime, ReplyFrom, ttl, Target, MaxHops, PingTimeoutMs);
68 | OnHostResult(result);
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/PingTest/PingTest/TraceRoute/TraceRouteHostResult.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Net;
5 | using System.Text;
6 |
7 | namespace PingTracer.TraceRoute
8 | {
9 | public class TraceRouteHostResult
10 | {
11 | ///
12 | /// Token originally provided to RouteTracer.TraceRoute()
13 | ///
14 | public object token;
15 | ///
16 | /// If true, the ping recieved a response.
17 | ///
18 | public bool success;
19 | ///
20 | /// The round-trip-time of the ping, in milliseconds (only if ).
21 | ///
22 | public long roundTripTime;
23 | ///
24 | /// The address a reply was received from (only if ).
25 | ///
26 | public IPAddress replyFrom;
27 | ///
28 | /// The ttl value which was sent with this ping.
29 | ///
30 | public byte ttl;
31 | ///
32 | /// The destination host of the ping (may differ from the address).
33 | ///
34 | public IPAddress target;
35 | ///
36 | /// Maximum ttl value which was used for the traceroute operation.
37 | ///
38 | public byte maxHops;
39 | ///
40 | /// Number of milliseconds after which the ping was instructed to time out.
41 | ///
42 | public int pingTimeoutMs;
43 |
44 | public TraceRouteHostResult(object token, bool success, long roundTripTime, IPAddress replyFrom, byte ttl, IPAddress target, byte maxHops, int pingTimeoutMs)
45 | {
46 | this.token = token;
47 | this.success = success;
48 | this.roundTripTime = roundTripTime;
49 | this.replyFrom = replyFrom;
50 | this.ttl = ttl;
51 | this.target = target;
52 | this.maxHops = maxHops;
53 | this.pingTimeoutMs = pingTimeoutMs;
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/PingTest/PingTest/Tracer/HostSettings.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Xml;
7 | using System.Xml.Serialization;
8 |
9 | namespace PingTracer.Tracer
10 | {
11 | ///
12 | /// Defines host(s) to ping, along with various related options.
13 | ///
14 | public class HostSettings
15 | {
16 | public string host;
17 | public string displayName = "";
18 | public int rate = 1;
19 | public bool pingsPerSecond = true;
20 | public bool doTraceRoute = true;
21 | public bool reverseDnsLookup = true;
22 | public bool drawServerNames = true;
23 | public bool drawLastPing = true;
24 | public bool drawAverage = true;
25 | public bool drawJitter = false;
26 | public bool drawMinMax = false;
27 | public bool drawPacketLoss = true;
28 | public bool drawLimitText = false;
29 | public int badThreshold = 100;
30 | public int worseThreshold = 200;
31 | public int upperLimit = 300;
32 | public int lowerLimit = 0;
33 | public int ScalingMethodID = 0;
34 | public bool preferIpv4 = true;
35 | public bool logFailures = true;
36 | public bool logSuccesses = false;
37 |
38 | public override bool Equals(object other)
39 | {
40 | if (other is HostSettings)
41 | {
42 | HostSettings o = (HostSettings)other;
43 | return host == o.host
44 | && rate == o.rate
45 | && pingsPerSecond == o.pingsPerSecond
46 | && doTraceRoute == o.doTraceRoute
47 | && reverseDnsLookup == o.reverseDnsLookup
48 | && drawServerNames == o.drawServerNames
49 | && drawMinMax == o.drawMinMax
50 | && drawPacketLoss == o.drawPacketLoss
51 | && badThreshold == o.badThreshold
52 | && worseThreshold == o.worseThreshold
53 | && lowerLimit == o.lowerLimit
54 | && upperLimit == o.upperLimit
55 | && ScalingMethodID == o.ScalingMethodID
56 | && preferIpv4 == o.preferIpv4;
57 | }
58 | return false;
59 | }
60 | public override int GetHashCode()
61 | {
62 | return host.GetHashCode() ^ rate.GetHashCode() ^ badThreshold.GetHashCode() ^ worseThreshold.GetHashCode() ^ lowerLimit.GetHashCode() ^ upperLimit.GetHashCode();
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/PingTest/PingTest/Tracer/PingController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace PingTracer.Tracer
8 | {
9 | ///
10 | /// Handles pinging of the host(s) defined by a Profile.
11 | ///
12 | public class PingController
13 | {
14 | ///
15 | /// Gets the Profile bound to this PingController.
16 | ///
17 | public readonly HostSettings p;
18 | public PingController(HostSettings p)
19 | {
20 | this.p = p;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/PingTest/PingTest/Tracert.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Collections.Generic;
4 | using System.Diagnostics;
5 | using System.Linq;
6 | using System.Net;
7 | using System.Net.NetworkInformation;
8 | using System.Net.Sockets;
9 | using System.Text;
10 | using System.Threading;
11 |
12 | namespace PingTracer
13 | {
14 | ///
15 | /// BASED ON http://www.fluxbytes.com/csharp/tracert-implementation-in-c/
16 | ///
17 | public static class Tracert
18 | {
19 | ///
20 | /// Traces the route which data have to travel through in order to reach an IP address.
21 | ///
22 | /// The IP address of the destination.
23 | /// Max hops to be returned.
24 | public static IEnumerable Trace(IPAddress address, int maxHops, int timeout)
25 | {
26 | // Max hops should be at least one or else there won't be any data to return.
27 | if (maxHops < 1)
28 | throw new ArgumentException("Max hops can't be lower than 1.");
29 |
30 | // Ensure that the timeout is not set to 0 or a negative number.
31 | if (timeout < 1)
32 | throw new ArgumentException("Timeout value must be higher than 0.");
33 |
34 |
35 | Ping ping = new Ping();
36 | PingOptions pingOptions = new PingOptions(1, true);
37 | Stopwatch pingReplyTime = new Stopwatch();
38 | PingReply reply;
39 | byte[] buffer = new byte[0];
40 | int consecutiveTimeouts = 0;
41 | do
42 | {
43 | pingReplyTime.Start();
44 | reply = ping.Send(address, timeout, buffer, pingOptions);
45 | pingReplyTime.Stop();
46 | IPAddress replyAddress = reply.Address;
47 | if (replyAddress != null && replyAddress.GetAddressBytes().All(b => b == 0))
48 | replyAddress = null;
49 | if (reply.Status == IPStatus.TimedOut)
50 | consecutiveTimeouts++;
51 | else
52 | consecutiveTimeouts = 0;
53 | // Return out TracertEntry object with all the information about the hop.
54 | yield return new TracertEntry()
55 | {
56 | HopID = pingOptions.Ttl,
57 | Address = replyAddress,
58 | ReplyTime = pingReplyTime.ElapsedMilliseconds,
59 | ReplyStatus = reply.Status
60 | };
61 |
62 | pingOptions.Ttl++;
63 | pingReplyTime.Reset();
64 | }
65 | while (reply.Status != IPStatus.Success && pingOptions.Ttl <= maxHops && consecutiveTimeouts < 5);
66 | }
67 |
68 | /////
69 | ///// Quickly traces the route which data have to travel through in order to reach an IP address. All hosts are pinged concurrently.
70 | /////
71 | ///// The IP address of the destination.
72 | ///// Max hops to be returned.
73 | //public static IEnumerable FastTrace(IPAddress address, int maxHops, int timeout)
74 | //{
75 | // // Max hops should be at least one or else there won't be any data to return.
76 | // if (maxHops < 1)
77 | // throw new ArgumentException("Max hops can't be lower than 1.");
78 |
79 | // // Ensure that the timeout is not set to 0 or a negative number.
80 | // if (timeout < 1)
81 | // throw new ArgumentException("Timeout value must be higher than 0.");
82 |
83 | // byte[] buffer = new byte[0];
84 | // ConcurrentQueue traceRouteReplies = new ConcurrentQueue();
85 | // for (int i = 1; i <= maxHops; i++)
86 | // {
87 | // Ping ping = PingInstancePool.Get();
88 | // ping.PingCompleted += ping_PingCompleted;
89 | // PingOptions pingOptions = new PingOptions(i, true);
90 | // Stopwatch pingReplyTime = new Stopwatch();
91 | // pingReplyTime.Start();
92 | // ping.SendAsync(address, timeout, buffer, pingOptions, new object[] { pingOptions, pingReplyTime, traceRouteReplies, ping });
93 | // }
94 | // while (traceRouteReplies.Count < maxHops)
95 | // Thread.Sleep(100);
96 | // SortedList sortedReplies = new SortedList();
97 | // TracertEntry reply;
98 | // while (traceRouteReplies.TryDequeue(out reply))
99 | // {
100 | // sortedReplies.Add(reply.HopID, reply);
101 | // }
102 | // foreach (int hopId in sortedReplies.Keys)
103 | // {
104 | // yield return sortedReplies[hopId];
105 | // if (sortedReplies[hopId].ReplyStatus == IPStatus.Success)
106 | // break;
107 | // }
108 | //}
109 |
110 | //static void ping_PingCompleted(object sender, PingCompletedEventArgs e)
111 | //{
112 | // Stopwatch pingReplyTime = (Stopwatch)((object[])e.UserState)[1];
113 | // pingReplyTime.Stop();
114 | // PingOptions pingOptions = (PingOptions)((object[])e.UserState)[0];
115 | // ConcurrentQueue traceRouteReplies = (ConcurrentQueue)((object[])e.UserState)[2];
116 | // Ping ping = (Ping)((object[])e.UserState)[3];
117 | // ping.PingCompleted -= ping_PingCompleted;
118 | // PingInstancePool.Recycle(ping);
119 |
120 | // string hostname = string.Empty;
121 | // if (e.Reply.Address != null)
122 | // {
123 | // try
124 | // {
125 | // hostname = Dns.GetHostByAddress(e.Reply.Address).HostName; // Retrieve the hostname for the replied address.
126 | // }
127 | // catch (SocketException) { /* No host available for that address. */ }
128 | // }
129 |
130 | // Console.WriteLine(pingOptions.Ttl + " " + e.Reply.Address);
131 | // traceRouteReplies.Enqueue(new TracertEntry()
132 | // {
133 | // HopID = pingOptions.Ttl,
134 | // Address = e.Reply.Address,
135 | // Hostname = hostname,
136 | // ReplyTime = pingReplyTime.ElapsedMilliseconds,
137 | // ReplyStatus = e.Reply.Status
138 | // });
139 | //}
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/PingTest/PingTest/TracertEntry.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Net;
4 | using System.Net.NetworkInformation;
5 | using System.Text;
6 |
7 | namespace PingTracer
8 | {
9 | ///
10 | /// BASED ON http://www.fluxbytes.com/csharp/tracert-implementation-in-c/
11 | ///
12 | public class TracertEntry
13 | {
14 | ///
15 | /// The hop id. Represents the number of the hop.
16 | ///
17 | public int HopID { get; set; }
18 |
19 | ///
20 | /// The IP address.
21 | ///
22 | public IPAddress Address { get; set; }
23 |
24 | ///
25 | /// The reply time it took for the host to receive and reply to the request in milliseconds.
26 | ///
27 | public long ReplyTime { get; set; }
28 |
29 | ///
30 | /// The reply status of the request.
31 | ///
32 | public IPStatus ReplyStatus { get; set; }
33 |
34 | public override string ToString()
35 | {
36 | string host;
37 | if (Address == null)
38 | host = "N/A";
39 | else
40 | host = Address.ToString();
41 | string status;
42 | if (ReplyStatus == IPStatus.TimedOut)
43 | status = "Request Timed Out";
44 | else
45 | status = ReplyTime.ToString() + " ms";
46 | return HopID + " | " + host + " | " + status;
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/PingTest/PingTest/Util/PingInstancePool.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Net.NetworkInformation;
6 | using System.Text;
7 |
8 | namespace PingTracer
9 | {
10 | public static class PingInstancePool
11 | {
12 | static ConcurrentQueue pool = new ConcurrentQueue();
13 | public static Ping Get()
14 | {
15 | Ping pinger;
16 | if (!pool.TryDequeue(out pinger))
17 | pinger = new Ping();
18 | return pinger;
19 | }
20 | public static void Recycle(Ping pinger)
21 | {
22 | pool.Enqueue(pinger);
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/PingTest/PingTest/Util/SimpleThreadPool.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading;
7 |
8 | ///
9 | /// From https://github.com/bp2008/BPUtil
10 | ///
11 | namespace PingTracer
12 | {
13 | class PoolThread
14 | {
15 | public Thread thread;
16 | public EventWaitHandle waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
17 | public volatile bool thisThreadHasWork = false;
18 | public CancellationToken cancellationToken;
19 | private CancellationTokenSource cancellationTokenSource;
20 |
21 | public PoolThread(Thread thread)
22 | {
23 | this.thread = thread;
24 | this.cancellationTokenSource = new CancellationTokenSource();
25 | this.cancellationToken = cancellationTokenSource.Token;
26 | }
27 | ///
28 | /// Sets the cancellationToken to the canceled state.
29 | ///
30 | public void Cancel()
31 | {
32 | if (!this.cancellationTokenSource.IsCancellationRequested)
33 | this.cancellationTokenSource.Cancel();
34 | }
35 | }
36 | public class SimpleThreadPool
37 | {
38 | ///
39 | /// A stack of threads that are idle.
40 | ///
41 | List idleThreads = new List();
42 | ///
43 | /// A queue of actions to be performed by threads.
44 | ///
45 | ConcurrentQueue actionQueue = new ConcurrentQueue();
46 | int threadTimeoutMilliseconds;
47 | int _currentMinThreads;
48 | int _currentMaxThreads;
49 | int _currentLiveThreads = 0;
50 | int _currentBusyThreads = 0;
51 | int threadNamingCounter = -1;
52 | bool threadsAreBackgroundThreads;
53 | string poolName;
54 | object threadLock = new object();
55 | volatile bool abort = false;
56 | ///
57 | /// Gets the number of threads that are currently available, including those which are busy and those which are idle.
58 | ///
59 | public int CurrentLiveThreads
60 | {
61 | get
62 | {
63 | return Thread.VolatileRead(ref _currentLiveThreads);
64 | }
65 | }
66 | ///
67 | /// Gets the number of threads that are currently busy processing actions.
68 | ///
69 | public int CurrentBusyThreads
70 | {
71 | get
72 | {
73 | return Thread.VolatileRead(ref _currentBusyThreads);
74 | }
75 | }
76 | ///
77 | /// Gets or sets the soft maximum number of threads this pool should have active at any given time. It is possible for there to be temporarily more threads than this if certain race conditions are met. If reducing the value, it may take some time for the number of threads to fall into line, as no special effort is taken to reduce the live thread count quickly.
78 | ///
79 | public int MaxThreads
80 | {
81 | get
82 | {
83 | return Thread.VolatileRead(ref _currentMaxThreads);
84 | }
85 | set
86 | {
87 | if (value < 1 || MinThreads > value)
88 | throw new Exception("MaxThreads must be >= 1 and >= MinThreads");
89 | Interlocked.Exchange(ref _currentMaxThreads, value);
90 | }
91 | }
92 | ///
93 | /// Gets or sets the minimum number of threads this pool should have active at any given time. If increasing the value, it may take some time for the number of threads to rise, as no special effort is taken to reach this number.
94 | ///
95 | public int MinThreads
96 | {
97 | get
98 | {
99 | return Thread.VolatileRead(ref _currentMinThreads);
100 | }
101 | set
102 | {
103 | if (value < 0 || value > MaxThreads)
104 | throw new Exception("MinThreads must be >= 0 and <= MaxThreads");
105 | Interlocked.Exchange(ref _currentMinThreads, value);
106 | }
107 | }
108 | ///
109 | ///
110 | ///
111 | ///
112 | /// The minimum number of threads that should be kept alive at all times.
113 | /// The largest number of threads this pool should attempt to have alive at any given time. It is possible for there to be temporarily more threads than this if certain race conditions are met.
114 | ///
115 | /// If true, the application will be able to exit without waiting for this thread pool. Background threads do not prevent a process from terminating. Once all foreground threads belonging to a process have terminated, the common language runtime ends the process. Any remaining background threads are stopped and do not complete.
116 | /// A method to use for logging exceptions. If null, SimpleHttpLogger.Log will be used.
117 | public SimpleThreadPool(string poolName, int minThreads = 6, int maxThreads = 32, int threadTimeoutMilliseconds = 60000, bool useBackgroundThreads = true)
118 | {
119 | this.poolName = poolName;
120 | this.threadTimeoutMilliseconds = threadTimeoutMilliseconds;
121 | if (minThreads < 0 || minThreads > maxThreads)
122 | throw new ArgumentException("minThreads must be >= 0 and <= maxThreads", "minThreads");
123 | if (maxThreads < 1 || minThreads > maxThreads)
124 | throw new ArgumentException("maxThreads must be >= 1 and >= minThreads", "maxThreads");
125 | this._currentMinThreads = minThreads;
126 | this._currentMaxThreads = maxThreads;
127 | this.threadsAreBackgroundThreads = useBackgroundThreads;
128 | SpawnNewIdleThreads(minThreads);
129 | }
130 | ///
131 | /// Creates new threads and signal them to begin working.
132 | ///
133 | ///
134 | private void SpawnNewActiveThreads(int count)
135 | {
136 | SpawnNewActiveOrIdleThreads(count, true);
137 | }
138 | ///
139 | /// Creates new thread but does not signal them. Instead, the threads are added to the pool of idle threads.
140 | ///
141 | ///
142 | private void SpawnNewIdleThreads(int count)
143 | {
144 | SpawnNewActiveOrIdleThreads(count, false);
145 | }
146 | private void SpawnNewActiveOrIdleThreads(int count, bool active)
147 | {
148 | if (abort)
149 | return;
150 | lock (threadLock)
151 | {
152 | for (int i = 0; i < count; i++)
153 | {
154 | if (CurrentLiveThreads < MaxThreads)
155 | {
156 | Interlocked.Increment(ref _currentLiveThreads);
157 | PoolThread pt = new PoolThread(new Thread(threadLoop));
158 | if (active)
159 | pt.waitHandle.Set();
160 | pt.thread.IsBackground = threadsAreBackgroundThreads;
161 | pt.thread.Name = poolName + " " + Interlocked.Increment(ref threadNamingCounter);
162 | pt.thread.Start(pt);
163 | if (!active)
164 | idleThreads.Add(pt);
165 | }
166 | }
167 | }
168 | }
169 | ///
170 | /// Aborts all idle threads, prevents the creation of new threads, and prevents new actions from being enqueued. This cannot be undone.
171 | ///
172 | public void Stop()
173 | {
174 | abort = true;
175 | lock (threadLock)
176 | {
177 | foreach (PoolThread pt in idleThreads)
178 | try
179 | {
180 | pt.Cancel();
181 | }
182 | catch (ThreadAbortException) { throw; }
183 | catch (Exception) { }
184 | }
185 | }
186 | public void Enqueue(Action action)
187 | {
188 | if (abort)
189 | return;
190 | actionQueue.Enqueue(action);
191 | if (!SignalTopThread())
192 | SpawnNewActiveThreads(1);
193 | }
194 |
195 | private bool SignalTopThread()
196 | {
197 | if (idleThreads.Count > 0)
198 | {
199 | lock (threadLock)
200 | {
201 | if (idleThreads.Count > 0)
202 | {
203 | PoolThread pt = idleThreads[idleThreads.Count - 1];
204 | idleThreads.RemoveAt(idleThreads.Count - 1);
205 | pt.thisThreadHasWork = true;
206 | pt.waitHandle.Set();
207 | return true;
208 | }
209 | }
210 | }
211 | return false;
212 | }
213 | private void threadLoop(object args)
214 | {
215 | try
216 | {
217 | PoolThread pt = (PoolThread)args;
218 | while (true)
219 | {
220 | // Wait for a signal
221 | if (WaitHandle.WaitAny(new WaitHandle[] { pt.waitHandle, pt.cancellationToken.WaitHandle }, threadTimeoutMilliseconds) != 0)
222 | {
223 | // Timeout has occurred. Make sure this thread has no work to perform before quitting.
224 | lock (threadLock)
225 | {
226 | bool isCancelled = pt.cancellationToken.IsCancellationRequested;
227 | if (!isCancelled && pt.thisThreadHasWork)
228 | {
229 | // This thread can't quit now because it has work to do.
230 | pt.thisThreadHasWork = false;
231 | }
232 | else if (!isCancelled && CurrentLiveThreads <= MinThreads)
233 | {
234 | // There is no work to do right now, but this thread is not allowed to quit.
235 | continue;
236 | }
237 | else
238 | {
239 | // This thread is allowed to quit
240 | Interlocked.Decrement(ref _currentLiveThreads);
241 | idleThreads.Remove(pt);
242 | return;
243 | }
244 | }
245 | }
246 | pt.thisThreadHasWork = false;
247 | // If we get here, this thread has been signaled or the timeout has expired but this thread was not allowed to quit.
248 | Interlocked.Increment(ref _currentBusyThreads);
249 | try
250 | {
251 | // Check for queued actions to perform.
252 | Action action;
253 | while (actionQueue.TryDequeue(out action))
254 | {
255 | try
256 | {
257 | action();
258 | }
259 | catch (ThreadAbortException) { throw; }
260 | catch (Exception) { }
261 | }
262 | }
263 | finally
264 | {
265 | Interlocked.Decrement(ref _currentBusyThreads);
266 | }
267 | // Return me to the pool
268 | lock (threadLock)
269 | {
270 | idleThreads.Add(pt);
271 | }
272 | }
273 | }
274 | catch (OperationCanceledException) { }
275 | catch (ThreadAbortException) { }
276 | catch (Exception) { }
277 | lock (threadLock)
278 | {
279 | Interlocked.Decrement(ref _currentLiveThreads);
280 | idleThreads.Remove((PoolThread)args);
281 | }
282 | }
283 | }
284 | }
285 |
--------------------------------------------------------------------------------
/PingTest/PingTest/WindowParams.cs:
--------------------------------------------------------------------------------
1 | namespace PingTracer
2 | {
3 | public class WindowParams
4 | {
5 | ///
6 | /// X coordinate
7 | ///
8 | public int X;
9 | ///
10 | /// Y coordinate
11 | ///
12 | public int Y;
13 | ///
14 | /// Width, ignore if less than 1.
15 | ///
16 | public int W;
17 | ///
18 | /// Height, ignore if less than 1.
19 | ///
20 | public int H;
21 | public WindowParams() { }
22 |
23 | public WindowParams(int x, int y, int w, int h)
24 | {
25 | X = x;
26 | Y = y;
27 | W = w;
28 | H = h;
29 | }
30 | public override string ToString()
31 | {
32 | return X + "," + Y + "," + W + "," + H;
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/PingTest/PingTest/include/SmartPingF.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bp2008/pingtracer/5b307f27d9214a2e290302318c5e94a9c175a9f2/PingTest/PingTest/include/SmartPingF.dll
--------------------------------------------------------------------------------
/PingTest/PingTest/include/Tracer.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace PingTracer.include
2 | {
3 | partial class Tracer
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 Windows Form 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.SuspendLayout();
32 | //
33 | // Tracer
34 | //
35 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
36 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
37 | this.ClientSize = new System.Drawing.Size(800, 450);
38 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
39 | this.Name = "Tracer";
40 | this.Text = "Tracer";
41 | this.Load += new System.EventHandler(this.Tracer_Load);
42 | this.ResumeLayout(false);
43 |
44 | }
45 |
46 | #endregion
47 | }
48 | }
--------------------------------------------------------------------------------
/PingTest/PingTest/include/Tracer.cs:
--------------------------------------------------------------------------------
1 | using PingTracer.Tracer;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.ComponentModel;
5 | using System.Data;
6 | using System.Drawing;
7 | using System.Linq;
8 | using System.Text;
9 | using System.Threading.Tasks;
10 | using System.Windows.Forms;
11 |
12 | namespace PingTracer.include
13 | {
14 | public partial class Tracer : Form
15 | {
16 | public Tracer(HostSettings p)
17 | {
18 | InitializeComponent();
19 | }
20 |
21 | private void Tracer_Load(object sender, EventArgs e)
22 | {
23 |
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/PingTest/PingTest/include/Tracer.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 |
--------------------------------------------------------------------------------
/PingTest/PingTest/pingtracer.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bp2008/pingtracer/5b307f27d9214a2e290302318c5e94a9c175a9f2/PingTest/PingTest/pingtracer.ico
--------------------------------------------------------------------------------
/PingTest/PingTracer.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.28729.10
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PingTracer", "PingTest\PingTracer.csproj", "{854FD525-4946-412E-9B3F-69412F505C30}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{740E81B9-D7FB-43D7-A5F0-D28E538B0E36}"
9 | ProjectSection(SolutionItems) = preProject
10 | .gitignore = .gitignore
11 | EndProjectSection
12 | EndProject
13 | Global
14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
15 | Debug|Any CPU = Debug|Any CPU
16 | Release|Any CPU = Release|Any CPU
17 | EndGlobalSection
18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
19 | {854FD525-4946-412E-9B3F-69412F505C30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
20 | {854FD525-4946-412E-9B3F-69412F505C30}.Debug|Any CPU.Build.0 = Debug|Any CPU
21 | {854FD525-4946-412E-9B3F-69412F505C30}.Release|Any CPU.ActiveCfg = Release|Any CPU
22 | {854FD525-4946-412E-9B3F-69412F505C30}.Release|Any CPU.Build.0 = Release|Any CPU
23 | EndGlobalSection
24 | GlobalSection(SolutionProperties) = preSolution
25 | HideSolutionNode = FALSE
26 | EndGlobalSection
27 | GlobalSection(ExtensibilityGlobals) = postSolution
28 | SolutionGuid = {CEEE6D15-D50F-4579-90BA-81E99E4BAF78}
29 | EndGlobalSection
30 | EndGlobal
31 |
--------------------------------------------------------------------------------
/PingTest/PingTracer.vssscc:
--------------------------------------------------------------------------------
1 | ""
2 | {
3 | "FILE_VERSION" = "9237"
4 | "ENLISTMENT_CHOICE" = "NEVER"
5 | "PROJECT_FILE_RELATIVE_PATH" = ""
6 | "NUMBER_OF_EXCLUDED_FILES" = "0"
7 | "ORIGINAL_PROJECT_FILE_PATH" = ""
8 | "NUMBER_OF_NESTED_PROJECTS" = "0"
9 | "SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROJECT"
10 | }
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ping Tracer
2 |
3 | Ping Tracer continuously pings each network host between your computer and a given destination, helping identify the source of connectivity problems.
4 |
5 | 
6 |
7 | This program helps to visually determine the origin of connection problems. The latency over time is shown on graphs, and each instance of packet loss is marked in red.
8 |
9 | A common use for such a tool is to monitor your connection to a multiplayer game server so you know who to blame when you experience lag. For example, if you experience a terrible moment of lag and you see that every node beyond your router is showing elevated latency or packet loss, then the lag was on your end. Typically, a poorly performing node will affect your connection to every node after it.
10 |
11 | I built this program for personal use, and decided to share it for free as an open source project. As such, it is light on features and polish.
12 |
13 | Notes about PingTracer's "Trace Route" implementation:
14 |
15 | * The trace route operation is not optimized for speed, and will take many seconds to complete if any hosts are unresponsive.
16 | * The trace route operation attempts to contact each host (a.k.a. network node) only once. Any host that fails to respond during the trace route operation will not be monitored.
17 | * The trace route operation is ended if 5 consecutive hosts fail to respond. This usually indicates that the destination host was already passed by and did not respond to the trace ping.
18 | * Some hosts respond to the traceroute but do not respond to direct pings. Such hosts are removed from monitoring after several seconds.
19 | * You can increase the ping rate as high as 10 pings per second (per host!) which can add up to dozens or even hundreds of pings per second. Please use responsibly.
20 | * Some routers implement [ICMP rate limiting](https://docs.paloaltonetworks.com/pan-os/10-0/pan-os-admin/networking/session-settings-and-timeouts/icmp/icmpv6-rate-limiting.html) in such a way that penalizes rapid pinging. Therefore, with higher ping rates you may see increased packet loss which is not representative of actual network performance.
21 |
22 | ## Installation
23 |
24 | Just download the latest release from [the releases tab](https://github.com/bp2008/pingtracer/releases) and extract it wherever you like.
25 |
26 | ## Keyboard shortcuts for the graphs
27 | Key | Alternate Key | action
28 | -:|-:|-
29 | Home | 9 | jump to beginning (first ping)
30 | End | 0 | jump to end (last ping)
31 | Page Up/Down | -/+ | move one page width to the left/right
32 |
33 |
34 |
35 | ## Command-Line arguments
36 | ```
37 | Arguments:
38 | -h
39 | Load a saved configuration with Display Name or Host field matching .
40 | If a matching configuration is not found, one will be created.
41 | -4
42 | (Use with -h) This indicates the "Prefer IPv4" checkbox must be checked.
43 | -6
44 | (Use with -h) This indicates the "Prefer IPv4" checkbox must be unchecked.
45 | -t0
46 | (Use with -h) This indicates the "Trace Route" checkbox must be unchecked.
47 | -t1
48 | (Use with -h) This indicates the "Trace Route" checkbox must be checked.
49 | -l x,y,w,h
50 | The window will be moved to the specified location and size.
51 | "w" and "h" parameters are optional.
52 | -s
53 | Pinging will begin automatically.
54 | -m
55 | The ping graphs will be maximized.
56 | ```
57 |
58 | See the latest command line arguments in-app with a usage example via Ping Tracer's `Tools` > `Command Line Arguments`.
59 |
60 |
61 | ## Building from source
62 |
63 | PingTracer (as of version 1.17) is a relatively simple C# and Windows Forms app with no third-party dependencies except .NET Framework 4.6.2. You should have no trouble building it in a standard installation of Visual Studio Community Edition.
64 |
--------------------------------------------------------------------------------