jt
2021-06-10 5d0d028456874576560552f5a5c4e8b801786f11
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
#region Apache License
//
// Licensed to the Apache Software Foundation (ASF) under one or more 
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership. 
// The ASF licenses this file to you under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with 
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#endregion
 
using System;
using System.Configuration;
using System.Reflection;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using System.Collections;
 
namespace log4net.Util
{
    /// <summary>
    /// Utility class for system specific information.
    /// </summary>
    /// <remarks>
    /// <para>
    /// Utility class of static methods for system specific information.
    /// </para>
    /// </remarks>
    /// <author>Nicko Cadell</author>
    /// <author>Gert Driesen</author>
    /// <author>Alexey Solofnenko</author>
    public sealed class SystemInfo
    {
        #region Private Constants
 
        private const string DEFAULT_NULL_TEXT = "(null)";
        private const string DEFAULT_NOT_AVAILABLE_TEXT = "NOT AVAILABLE";
 
        #endregion
 
        #region Private Instance Constructors
 
        /// <summary>
        /// Private constructor to prevent instances.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Only static methods are exposed from this type.
        /// </para>
        /// </remarks>
        private SystemInfo() 
        {
        }
 
        #endregion Private Instance Constructors
 
        #region Public Static Constructor
 
        /// <summary>
        /// Initialize default values for private static fields.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Only static methods are exposed from this type.
        /// </para>
        /// </remarks>
        static SystemInfo()
        {
            string nullText = DEFAULT_NULL_TEXT;
            string notAvailableText = DEFAULT_NOT_AVAILABLE_TEXT;
 
#if !NETCF
            // Look for log4net.NullText in AppSettings
            string nullTextAppSettingsKey = SystemInfo.GetAppSetting("log4net.NullText");
            if (nullTextAppSettingsKey != null && nullTextAppSettingsKey.Length > 0)
            {
                LogLog.Debug(declaringType, "Initializing NullText value to [" + nullTextAppSettingsKey + "].");
                nullText = nullTextAppSettingsKey;
            }
 
            // Look for log4net.NotAvailableText in AppSettings
            string notAvailableTextAppSettingsKey = SystemInfo.GetAppSetting("log4net.NotAvailableText");
            if (notAvailableTextAppSettingsKey != null && notAvailableTextAppSettingsKey.Length > 0)
            {
                LogLog.Debug(declaringType, "Initializing NotAvailableText value to [" + notAvailableTextAppSettingsKey + "].");
                notAvailableText = notAvailableTextAppSettingsKey;
            }
#endif
            s_notAvailableText = notAvailableText;
            s_nullText = nullText;
        }
 
        #endregion
 
        #region Public Static Properties
 
        /// <summary>
        /// Gets the system dependent line terminator.
        /// </summary>
        /// <value>
        /// The system dependent line terminator.
        /// </value>
        /// <remarks>
        /// <para>
        /// Gets the system dependent line terminator.
        /// </para>
        /// </remarks>
        public static string NewLine
        {
            get
            {
#if NETCF
                return "\r\n";
#else
                return System.Environment.NewLine;
#endif
            }
        }
 
        /// <summary>
        /// Gets the base directory for this <see cref="AppDomain"/>.
        /// </summary>
        /// <value>The base directory path for the current <see cref="AppDomain"/>.</value>
        /// <remarks>
        /// <para>
        /// Gets the base directory for this <see cref="AppDomain"/>.
        /// </para>
        /// <para>
        /// The value returned may be either a local file path or a URI.
        /// </para>
        /// </remarks>
        public static string ApplicationBaseDirectory
        {
            get 
            {
#if NETCF
                return System.IO.Path.GetDirectoryName(SystemInfo.EntryAssemblyLocation) + System.IO.Path.DirectorySeparatorChar;
#else
                return AppDomain.CurrentDomain.BaseDirectory;
#endif
            }
        }
 
        /// <summary>
        /// Gets the path to the configuration file for the current <see cref="AppDomain"/>.
        /// </summary>
        /// <value>The path to the configuration file for the current <see cref="AppDomain"/>.</value>
        /// <remarks>
        /// <para>
        /// The .NET Compact Framework 1.0 does not have a concept of a configuration
        /// file. For this runtime, we use the entry assembly location as the root for
        /// the configuration file name.
        /// </para>
        /// <para>
        /// The value returned may be either a local file path or a URI.
        /// </para>
        /// </remarks>
        public static string ConfigurationFileLocation
        {
            get 
            {
#if NETCF
                return SystemInfo.EntryAssemblyLocation+".config";
#else
                return System.AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
#endif
            }
        }
 
        /// <summary>
        /// Gets the path to the file that first executed in the current <see cref="AppDomain"/>.
        /// </summary>
        /// <value>The path to the entry assembly.</value>
        /// <remarks>
        /// <para>
        /// Gets the path to the file that first executed in the current <see cref="AppDomain"/>.
        /// </para>
        /// </remarks>
        public static string EntryAssemblyLocation
        {
            get 
            {
#if NETCF
                return SystemInfo.NativeEntryAssemblyLocation;
#else
                return System.Reflection.Assembly.GetEntryAssembly().Location;
#endif
            }
        }
 
        /// <summary>
        /// Gets the ID of the current thread.
        /// </summary>
        /// <value>The ID of the current thread.</value>
        /// <remarks>
        /// <para>
        /// On the .NET framework, the <c>AppDomain.GetCurrentThreadId</c> method
        /// is used to obtain the thread ID for the current thread. This is the 
        /// operating system ID for the thread.
        /// </para>
        /// <para>
        /// On the .NET Compact Framework 1.0 it is not possible to get the 
        /// operating system thread ID for the current thread. The native method 
        /// <c>GetCurrentThreadId</c> is implemented inline in a header file
        /// and cannot be called.
        /// </para>
        /// <para>
        /// On the .NET Framework 2.0 the <c>Thread.ManagedThreadId</c> is used as this
        /// gives a stable id unrelated to the operating system thread ID which may 
        /// change if the runtime is using fibers.
        /// </para>
        /// </remarks>
        public static int CurrentThreadId
        {
            get 
            {
#if NETCF_1_0
                return System.Threading.Thread.CurrentThread.GetHashCode();
#elif NET_2_0 || NETCF_2_0 || MONO_2_0
                return System.Threading.Thread.CurrentThread.ManagedThreadId;
#else
                return AppDomain.GetCurrentThreadId();
#endif
            }
        }
 
        /// <summary>
        /// Get the host name or machine name for the current machine
        /// </summary>
        /// <value>
        /// The hostname or machine name
        /// </value>
        /// <remarks>
        /// <para>
        /// Get the host name or machine name for the current machine
        /// </para>
        /// <para>
        /// The host name (<see cref="System.Net.Dns.GetHostName"/>) or
        /// the machine name (<c>Environment.MachineName</c>) for
        /// the current machine, or if neither of these are available
        /// then <c>NOT AVAILABLE</c> is returned.
        /// </para>
        /// </remarks>
        public static string HostName
        {
            get
            {
                if (s_hostName == null)
                {
 
                    // Get the DNS host name of the current machine
                    try
                    {
                        // Lookup the host name
                        s_hostName = System.Net.Dns.GetHostName();
                    }
                    catch(System.Net.Sockets.SocketException)
                    {
                    }
                    catch(System.Security.SecurityException)
                    {
                        // We may get a security exception looking up the hostname
                        // You must have Unrestricted DnsPermission to access resource
                    }
 
                    // Get the NETBIOS machine name of the current machine
                    if (s_hostName == null || s_hostName.Length == 0)
                    {
                        try
                        {
#if (!SSCLI && !NETCF)
                            s_hostName = Environment.MachineName;
#endif
                        }
                        catch(InvalidOperationException)
                        {
                        }
                        catch(System.Security.SecurityException)
                        {
                            // We may get a security exception looking up the machine name
                            // You must have Unrestricted EnvironmentPermission to access resource
                        }
                    }
 
                    // Couldn't find a value
                    if (s_hostName == null || s_hostName.Length == 0)
                    {
                        s_hostName = s_notAvailableText;
                    }
                }
                return s_hostName;
            }
        }
 
        /// <summary>
        /// Get this application's friendly name
        /// </summary>
        /// <value>
        /// The friendly name of this application as a string
        /// </value>
        /// <remarks>
        /// <para>
        /// If available the name of the application is retrieved from
        /// the <c>AppDomain</c> using <c>AppDomain.CurrentDomain.FriendlyName</c>.
        /// </para>
        /// <para>
        /// Otherwise the file name of the entry assembly is used.
        /// </para>
        /// </remarks>
        public static string ApplicationFriendlyName
        {
            get
            {
                if (s_appFriendlyName == null)
                {
                    try
                    {
#if !NETCF
                        s_appFriendlyName = AppDomain.CurrentDomain.FriendlyName;
#endif
                    }
                    catch(System.Security.SecurityException)
                    {
                        // This security exception will occur if the caller does not have 
                        // some undefined set of SecurityPermission flags.
                        LogLog.Debug(declaringType, "Security exception while trying to get current domain friendly name. Error Ignored.");
                    }
 
                    if (s_appFriendlyName == null || s_appFriendlyName.Length == 0)
                    {
                        try
                        {
                            string assemblyLocation = SystemInfo.EntryAssemblyLocation;
                            s_appFriendlyName = System.IO.Path.GetFileName(assemblyLocation);
                        }
                        catch(System.Security.SecurityException)
                        {
                            // Caller needs path discovery permission
                        }
                    }
 
                    if (s_appFriendlyName == null || s_appFriendlyName.Length == 0)
                    {
                        s_appFriendlyName = s_notAvailableText;
                    }
                }
                return s_appFriendlyName;
            }
        }
 
        /// <summary>
        /// Get the start time for the current process.
        /// </summary>
        /// <remarks>
        /// <para>
        /// This is the time at which the log4net library was loaded into the
        /// AppDomain. Due to reports of a hang in the call to <c>System.Diagnostics.Process.StartTime</c>
        /// this is not the start time for the current process.
        /// </para>
        /// <para>
        /// The log4net library should be loaded by an application early during its
        /// startup, therefore this start time should be a good approximation for
        /// the actual start time.
        /// </para>
        /// <para>
        /// Note that AppDomains may be loaded and unloaded within the
        /// same process without the process terminating, however this start time
        /// will be set per AppDomain.
        /// </para>
        /// </remarks>
        public static DateTime ProcessStartTime
        {
            get { return s_processStartTime; }
        }
 
        /// <summary>
        /// Text to output when a <c>null</c> is encountered.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Use this value to indicate a <c>null</c> has been encountered while
        /// outputting a string representation of an item.
        /// </para>
        /// <para>
        /// The default value is <c>(null)</c>. This value can be overridden by specifying
        /// a value for the <c>log4net.NullText</c> appSetting in the application's
        /// .config file.
        /// </para>
        /// </remarks>
        public static string NullText
        {
            get { return s_nullText; }
            set { s_nullText = value; }
        }
 
        /// <summary>
        /// Text to output when an unsupported feature is requested.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Use this value when an unsupported feature is requested.
        /// </para>
        /// <para>
        /// The default value is <c>NOT AVAILABLE</c>. This value can be overridden by specifying
        /// a value for the <c>log4net.NotAvailableText</c> appSetting in the application's
        /// .config file.
        /// </para>
        /// </remarks>
        public static string NotAvailableText
        {
            get { return s_notAvailableText; }
            set { s_notAvailableText = value; }
        }
 
        #endregion Public Static Properties
 
        #region Public Static Methods
 
        /// <summary>
        /// Gets the assembly location path for the specified assembly.
        /// </summary>
        /// <param name="myAssembly">The assembly to get the location for.</param>
        /// <returns>The location of the assembly.</returns>
        /// <remarks>
        /// <para>
        /// This method does not guarantee to return the correct path
        /// to the assembly. If only tries to give an indication as to
        /// where the assembly was loaded from.
        /// </para>
        /// </remarks>
        public static string AssemblyLocationInfo(Assembly myAssembly)
        {
#if NETCF
            return "Not supported on Microsoft .NET Compact Framework";
#else
            if (myAssembly.GlobalAssemblyCache)
            {
                return "Global Assembly Cache";
            }
            else
            {
                try
                {
                    // This call requires FileIOPermission for access to the path
                    // if we don't have permission then we just ignore it and
                    // carry on.
                    return myAssembly.Location;
                }
                catch(System.Security.SecurityException)
                {
                    return "Location Permission Denied";
                }
            }
#endif
        }
 
        /// <summary>
        /// Gets the fully qualified name of the <see cref="Type" />, including 
        /// the name of the assembly from which the <see cref="Type" /> was 
        /// loaded.
        /// </summary>
        /// <param name="type">The <see cref="Type" /> to get the fully qualified name for.</param>
        /// <returns>The fully qualified name for the <see cref="Type" />.</returns>
        /// <remarks>
        /// <para>
        /// This is equivalent to the <c>Type.AssemblyQualifiedName</c> property,
        /// but this method works on the .NET Compact Framework 1.0 as well as
        /// the full .NET runtime.
        /// </para>
        /// </remarks>
        public static string AssemblyQualifiedName(Type type)
        {
            return type.FullName + ", " + type.Assembly.FullName;
        }
 
        /// <summary>
        /// Gets the short name of the <see cref="Assembly" />.
        /// </summary>
        /// <param name="myAssembly">The <see cref="Assembly" /> to get the name for.</param>
        /// <returns>The short name of the <see cref="Assembly" />.</returns>
        /// <remarks>
        /// <para>
        /// The short name of the assembly is the <see cref="Assembly.FullName" /> 
        /// without the version, culture, or public key. i.e. it is just the 
        /// assembly's file name without the extension.
        /// </para>
        /// <para>
        /// Use this rather than <c>Assembly.GetName().Name</c> because that
        /// is not available on the Compact Framework.
        /// </para>
        /// <para>
        /// Because of a FileIOPermission security demand we cannot do
        /// the obvious Assembly.GetName().Name. We are allowed to get
        /// the <see cref="Assembly.FullName" /> of the assembly so we 
        /// start from there and strip out just the assembly name.
        /// </para>
        /// </remarks>
        public static string AssemblyShortName(Assembly myAssembly)
        {
            string name = myAssembly.FullName;
            int offset = name.IndexOf(',');
            if (offset > 0)
            {
                name = name.Substring(0, offset);
            }
            return name.Trim();
 
            // TODO: Do we need to unescape the assembly name string? 
            // Doc says '\' is an escape char but has this already been 
            // done by the string loader?
        }
 
        /// <summary>
        /// Gets the file name portion of the <see cref="Assembly" />, including the extension.
        /// </summary>
        /// <param name="myAssembly">The <see cref="Assembly" /> to get the file name for.</param>
        /// <returns>The file name of the assembly.</returns>
        /// <remarks>
        /// <para>
        /// Gets the file name portion of the <see cref="Assembly" />, including the extension.
        /// </para>
        /// </remarks>
        public static string AssemblyFileName(Assembly myAssembly)
        {
#if NETCF
            // This is not very good because it assumes that only
            // the entry assembly can be an EXE. In fact multiple
            // EXEs can be loaded in to a process.
 
            string assemblyShortName = SystemInfo.AssemblyShortName(myAssembly);
            string entryAssemblyShortName = System.IO.Path.GetFileNameWithoutExtension(SystemInfo.EntryAssemblyLocation);
 
            if (string.Compare(assemblyShortName, entryAssemblyShortName, true) == 0)
            {
                // assembly is entry assembly
                return assemblyShortName + ".exe";
            }
            else
            {
                // assembly is not entry assembly
                return assemblyShortName + ".dll";
            }
#else
            return System.IO.Path.GetFileName(myAssembly.Location);
#endif
        }
 
        /// <summary>
        /// Loads the type specified in the type string.
        /// </summary>
        /// <param name="relativeType">A sibling type to use to load the type.</param>
        /// <param name="typeName">The name of the type to load.</param>
        /// <param name="throwOnError">Flag set to <c>true</c> to throw an exception if the type cannot be loaded.</param>
        /// <param name="ignoreCase"><c>true</c> to ignore the case of the type name; otherwise, <c>false</c></param>
        /// <returns>The type loaded or <c>null</c> if it could not be loaded.</returns>
        /// <remarks>
        /// <para>
        /// If the type name is fully qualified, i.e. if contains an assembly name in 
        /// the type name, the type will be loaded from the system using 
        /// <see cref="Type.GetType(string,bool)"/>.
        /// </para>
        /// <para>
        /// If the type name is not fully qualified, it will be loaded from the assembly
        /// containing the specified relative type. If the type is not found in the assembly 
        /// then all the loaded assemblies will be searched for the type.
        /// </para>
        /// </remarks>
        public static Type GetTypeFromString(Type relativeType, string typeName, bool throwOnError, bool ignoreCase)
        {
            return GetTypeFromString(relativeType.Assembly, typeName, throwOnError, ignoreCase);
        }
 
        /// <summary>
        /// Loads the type specified in the type string.
        /// </summary>
        /// <param name="typeName">The name of the type to load.</param>
        /// <param name="throwOnError">Flag set to <c>true</c> to throw an exception if the type cannot be loaded.</param>
        /// <param name="ignoreCase"><c>true</c> to ignore the case of the type name; otherwise, <c>false</c></param>
        /// <returns>The type loaded or <c>null</c> if it could not be loaded.</returns>        
        /// <remarks>
        /// <para>
        /// If the type name is fully qualified, i.e. if contains an assembly name in 
        /// the type name, the type will be loaded from the system using 
        /// <see cref="Type.GetType(string,bool)"/>.
        /// </para>
        /// <para>
        /// If the type name is not fully qualified it will be loaded from the
        /// assembly that is directly calling this method. If the type is not found 
        /// in the assembly then all the loaded assemblies will be searched for the type.
        /// </para>
        /// </remarks>
        public static Type GetTypeFromString(string typeName, bool throwOnError, bool ignoreCase)
        {
            return GetTypeFromString(Assembly.GetCallingAssembly(), typeName, throwOnError, ignoreCase);
        }
 
        /// <summary>
        /// Loads the type specified in the type string.
        /// </summary>
        /// <param name="relativeAssembly">An assembly to load the type from.</param>
        /// <param name="typeName">The name of the type to load.</param>
        /// <param name="throwOnError">Flag set to <c>true</c> to throw an exception if the type cannot be loaded.</param>
        /// <param name="ignoreCase"><c>true</c> to ignore the case of the type name; otherwise, <c>false</c></param>
        /// <returns>The type loaded or <c>null</c> if it could not be loaded.</returns>
        /// <remarks>
        /// <para>
        /// If the type name is fully qualified, i.e. if contains an assembly name in 
        /// the type name, the type will be loaded from the system using 
        /// <see cref="Type.GetType(string,bool)"/>.
        /// </para>
        /// <para>
        /// If the type name is not fully qualified it will be loaded from the specified
        /// assembly. If the type is not found in the assembly then all the loaded assemblies 
        /// will be searched for the type.
        /// </para>
        /// </remarks>
        public static Type GetTypeFromString(Assembly relativeAssembly, string typeName, bool throwOnError, bool ignoreCase)
        {
            // Check if the type name specifies the assembly name
            if(typeName.IndexOf(',') == -1)
            {
                //LogLog.Debug(declaringType, "SystemInfo: Loading type ["+typeName+"] from assembly ["+relativeAssembly.FullName+"]");
#if NETCF
                return relativeAssembly.GetType(typeName, throwOnError);
#else
                // Attempt to lookup the type from the relativeAssembly
                Type type = relativeAssembly.GetType(typeName, false, ignoreCase);
                if (type != null)
                {
                    // Found type in relative assembly
                    //LogLog.Debug(declaringType, "SystemInfo: Loaded type ["+typeName+"] from assembly ["+relativeAssembly.FullName+"]");
                    return type;
                }
 
                Assembly[] loadedAssemblies = null;
                try
                {
                    loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
                }
                catch(System.Security.SecurityException)
                {
                    // Insufficient permissions to get the list of loaded assemblies
                }
 
                if (loadedAssemblies != null)
                {
                    // Search the loaded assemblies for the type
                    foreach (Assembly assembly in loadedAssemblies) 
                    {
                        type = assembly.GetType(typeName, false, ignoreCase);
                        if (type != null)
                        {
                            // Found type in loaded assembly
                            LogLog.Debug(declaringType, "Loaded type ["+typeName+"] from assembly ["+assembly.FullName+"] by searching loaded assemblies.");
                            return type;
                        }
                    }
                }
 
                // Didn't find the type
                if (throwOnError)
                {
                    throw new TypeLoadException("Could not load type ["+typeName+"]. Tried assembly ["+relativeAssembly.FullName+"] and all loaded assemblies");
                }
                return null;
#endif
            }
            else
            {
                // Includes explicit assembly name
                //LogLog.Debug(declaringType, "SystemInfo: Loading type ["+typeName+"] from global Type");
 
#if NETCF
                // In NETCF 2 and 3 arg versions seem to behave differently
                // https://issues.apache.org/jira/browse/LOG4NET-113
                return Type.GetType(typeName, throwOnError);
#else
                return Type.GetType(typeName, throwOnError, ignoreCase);
#endif
            }
        }
 
 
        /// <summary>
        /// Generate a new guid
        /// </summary>
        /// <returns>A new Guid</returns>
        /// <remarks>
        /// <para>
        /// Generate a new guid
        /// </para>
        /// </remarks>
        public static Guid NewGuid()
        {
#if NETCF_1_0
            return PocketGuid.NewGuid();
#else
            return Guid.NewGuid();
#endif
        }
 
        /// <summary>
        /// Create an <see cref="ArgumentOutOfRangeException"/>
        /// </summary>
        /// <param name="parameterName">The name of the parameter that caused the exception</param>
        /// <param name="actualValue">The value of the argument that causes this exception</param>
        /// <param name="message">The message that describes the error</param>
        /// <returns>the ArgumentOutOfRangeException object</returns>
        /// <remarks>
        /// <para>
        /// Create a new instance of the <see cref="ArgumentOutOfRangeException"/> class 
        /// with a specified error message, the parameter name, and the value 
        /// of the argument.
        /// </para>
        /// <para>
        /// The Compact Framework does not support the 3 parameter constructor for the
        /// <see cref="ArgumentOutOfRangeException"/> type. This method provides an
        /// implementation that works for all platforms.
        /// </para>
        /// </remarks>
        public static ArgumentOutOfRangeException CreateArgumentOutOfRangeException(string parameterName, object actualValue, string message)
        {
#if NETCF_1_0
            return new ArgumentOutOfRangeException(message + " [param=" + parameterName + "] [value=" + actualValue + "]");
#elif NETCF_2_0
            return new ArgumentOutOfRangeException(parameterName, message + " [value=" + actualValue + "]");
#else
            return new ArgumentOutOfRangeException(parameterName, actualValue, message);
#endif
        }
 
 
        /// <summary>
        /// Parse a string into an <see cref="Int32"/> value
        /// </summary>
        /// <param name="s">the string to parse</param>
        /// <param name="val">out param where the parsed value is placed</param>
        /// <returns><c>true</c> if the string was able to be parsed into an integer</returns>
        /// <remarks>
        /// <para>
        /// Attempts to parse the string into an integer. If the string cannot
        /// be parsed then this method returns <c>false</c>. The method does not throw an exception.
        /// </para>
        /// </remarks>
        public static bool TryParse(string s, out int val)
        {
#if NETCF
            val = 0;
            try
            {
                val = int.Parse(s, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture);
                return true;
            }
            catch
            {
            }
 
            return false;
#else
            // Initialise out param
            val = 0;
 
            try
            {
                double doubleVal;
                if (Double.TryParse(s, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out doubleVal))
                {
                    val = Convert.ToInt32(doubleVal);
                    return true;
                }
            }
            catch
            {
                // Ignore exception, just return false
            }
 
            return false;
#endif
        }
 
        /// <summary>
        /// Parse a string into an <see cref="Int64"/> value
        /// </summary>
        /// <param name="s">the string to parse</param>
        /// <param name="val">out param where the parsed value is placed</param>
        /// <returns><c>true</c> if the string was able to be parsed into an integer</returns>
        /// <remarks>
        /// <para>
        /// Attempts to parse the string into an integer. If the string cannot
        /// be parsed then this method returns <c>false</c>. The method does not throw an exception.
        /// </para>
        /// </remarks>
        public static bool TryParse(string s, out long val)
        {
#if NETCF
            val = 0;
            try
            {
                val = long.Parse(s, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture);
                return true;
            }
            catch
            {
            }
 
            return false;
#else
            // Initialise out param
            val = 0;
 
            try
            {
                double doubleVal;
                if (Double.TryParse(s, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out doubleVal))
                {
                    val = Convert.ToInt64(doubleVal);
                    return true;
                }
            }
            catch
            {
                // Ignore exception, just return false
            }
 
            return false;
#endif
        }
 
        /// <summary>
        /// Parse a string into an <see cref="Int16"/> value
        /// </summary>
        /// <param name="s">the string to parse</param>
        /// <param name="val">out param where the parsed value is placed</param>
        /// <returns><c>true</c> if the string was able to be parsed into an integer</returns>
        /// <remarks>
        /// <para>
        /// Attempts to parse the string into an integer. If the string cannot
        /// be parsed then this method returns <c>false</c>. The method does not throw an exception.
        /// </para>
        /// </remarks>
        public static bool TryParse(string s, out short val)
        {
#if NETCF
            val = 0;
            try
            {
                val = short.Parse(s, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture);
                return true;
            }
            catch
            {
            }
 
            return false;
#else
            // Initialise out param
            val = 0;
 
            try 
            {
                double doubleVal;
                if (Double.TryParse(s, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out doubleVal))
                {
                    val = Convert.ToInt16(doubleVal);
                    return true;
                }
            }
            catch
            {
                // Ignore exception, just return false
            }
 
            return false;
#endif
        }
 
        /// <summary>
        /// Lookup an application setting
        /// </summary>
        /// <param name="key">the application settings key to lookup</param>
        /// <returns>the value for the key, or <c>null</c></returns>
        /// <remarks>
        /// <para>
        /// Configuration APIs are not supported under the Compact Framework
        /// </para>
        /// </remarks>
        public static string GetAppSetting(string key)
        {
            try
            {
#if NETCF
                // Configuration APIs are not suported under the Compact Framework
#elif NET_2_0
                return ConfigurationManager.AppSettings[key];
#else
                return ConfigurationSettings.AppSettings[key];
#endif
            }
            catch(Exception ex)
            {
                // If an exception is thrown here then it looks like the config file does not parse correctly.
                LogLog.Error(declaringType, "Exception while reading ConfigurationSettings. Check your .config file is well formed XML.", ex);
            }
            return null;
        }
 
        /// <summary>
        /// Convert a path into a fully qualified local file path.
        /// </summary>
        /// <param name="path">The path to convert.</param>
        /// <returns>The fully qualified path.</returns>
        /// <remarks>
        /// <para>
        /// Converts the path specified to a fully
        /// qualified path. If the path is relative it is
        /// taken as relative from the application base 
        /// directory.
        /// </para>
        /// <para>
        /// The path specified must be a local file path, a URI is not supported.
        /// </para>
        /// </remarks>
        public static string ConvertToFullPath(string path)
        {
            if (path == null)
            {
                throw new ArgumentNullException("path");
            }
 
            string baseDirectory = "";
            try
            {
                string applicationBaseDirectory = SystemInfo.ApplicationBaseDirectory;
                if (applicationBaseDirectory != null)
                {
                    // applicationBaseDirectory may be a URI not a local file path
                    Uri applicationBaseDirectoryUri = new Uri(applicationBaseDirectory);
                    if (applicationBaseDirectoryUri.IsFile)
                    {
                        baseDirectory = applicationBaseDirectoryUri.LocalPath;
                    }
                }
            }
            catch
            {
                // Ignore URI exceptions & SecurityExceptions from SystemInfo.ApplicationBaseDirectory
            }
 
            if (baseDirectory != null && baseDirectory.Length > 0)
            {
                // Note that Path.Combine will return the second path if it is rooted
                return Path.GetFullPath(Path.Combine(baseDirectory, path));
            }
            return Path.GetFullPath(path);
        }
 
        /// <summary>
        /// Creates a new case-insensitive instance of the <see cref="Hashtable"/> class with the default initial capacity. 
        /// </summary>
        /// <returns>A new case-insensitive instance of the <see cref="Hashtable"/> class with the default initial capacity</returns>
        /// <remarks>
        /// <para>
        /// The new Hashtable instance uses the default load factor, the CaseInsensitiveHashCodeProvider, and the CaseInsensitiveComparer.
        /// </para>
        /// </remarks>
        public static Hashtable CreateCaseInsensitiveHashtable()
        {
#if NETCF_1_0
            return new Hashtable(CaseInsensitiveHashCodeProvider.Default, CaseInsensitiveComparer.Default);
#elif NETCF_2_0 || NET_2_0 || MONO_2_0
            return new Hashtable(StringComparer.OrdinalIgnoreCase);
#else
            return System.Collections.Specialized.CollectionsUtil.CreateCaseInsensitiveHashtable();
#endif
        }
 
        #endregion Public Static Methods
 
        #region Private Static Methods
 
#if NETCF
        private static string NativeEntryAssemblyLocation 
        {
            get 
            {
                StringBuilder moduleName = null;
 
                IntPtr moduleHandle = GetModuleHandle(IntPtr.Zero);
 
                if (moduleHandle != IntPtr.Zero) 
                {
                    moduleName = new StringBuilder(255);
                    if (GetModuleFileName(moduleHandle, moduleName,    moduleName.Capacity) == 0) 
                    {
                        throw new NotSupportedException(NativeError.GetLastError().ToString());
                    }
                } 
                else 
                {
                    throw new NotSupportedException(NativeError.GetLastError().ToString());
                }
 
                return moduleName.ToString();
            }
        }
 
        [DllImport("CoreDll.dll", SetLastError=true, CharSet=CharSet.Unicode)]
        private static extern IntPtr GetModuleHandle(IntPtr ModuleName);
 
        [DllImport("CoreDll.dll", SetLastError=true, CharSet=CharSet.Unicode)]
        private static extern Int32 GetModuleFileName(
            IntPtr hModule,
            StringBuilder ModuleName,
            Int32 cch);
 
#endif
 
        #endregion Private Static Methods
 
        #region Public Static Fields
 
        /// <summary>
        /// Gets an empty array of types.
        /// </summary>
        /// <remarks>
        /// <para>
        /// The <c>Type.EmptyTypes</c> field is not available on
        /// the .NET Compact Framework 1.0.
        /// </para>
        /// </remarks>
        public static readonly Type[] EmptyTypes = new Type[0];
 
        #endregion Public Static Fields
 
        #region Private Static Fields
 
        /// <summary>
        /// The fully qualified type of the SystemInfo class.
        /// </summary>
        /// <remarks>
        /// Used by the internal logger to record the Type of the
        /// log message.
        /// </remarks>
        private readonly static Type declaringType = typeof(SystemInfo);
 
        /// <summary>
        /// Cache the host name for the current machine
        /// </summary>
        private static string s_hostName;
 
        /// <summary>
        /// Cache the application friendly name
        /// </summary>
        private static string s_appFriendlyName;
 
        /// <summary>
        /// Text to output when a <c>null</c> is encountered.
        /// </summary>
        private static string s_nullText;
 
        /// <summary>
        /// Text to output when an unsupported feature is requested.
        /// </summary>
        private static string s_notAvailableText;
 
        /// <summary>
        /// Start time for the current process.
        /// </summary>
        private static DateTime s_processStartTime = DateTime.Now;
 
        #endregion
 
        #region Compact Framework Helper Classes
#if NETCF_1_0
        /// <summary>
        /// Generate GUIDs on the .NET Compact Framework.
        /// </summary>
        public class PocketGuid
        {
            // guid variant types
            private enum GuidVariant
            {
                ReservedNCS = 0x00,
                Standard = 0x02,
                ReservedMicrosoft = 0x06,
                ReservedFuture = 0x07
            }
 
            // guid version types
            private enum GuidVersion
            {
                TimeBased = 0x01,
                Reserved = 0x02,
                NameBased = 0x03,
                Random = 0x04
            }
 
            // constants that are used in the class
            private class Const
            {
                // number of bytes in guid
                public const int ByteArraySize = 16;
 
                // multiplex variant info
                public const int VariantByte = 8;
                public const int VariantByteMask = 0x3f;
                public const int VariantByteShift = 6;
 
                // multiplex version info
                public const int VersionByte = 7;
                public const int VersionByteMask = 0x0f;
                public const int VersionByteShift = 4;
            }
 
            // imports for the crypto api functions
            private class WinApi
            {
                public const uint PROV_RSA_FULL = 1;
                public const uint CRYPT_VERIFYCONTEXT = 0xf0000000;
 
                [DllImport("CoreDll.dll")] 
                public static extern bool CryptAcquireContext(
                    ref IntPtr phProv, string pszContainer, string pszProvider,
                    uint dwProvType, uint dwFlags);
 
                [DllImport("CoreDll.dll")] 
                public static extern bool CryptReleaseContext( 
                    IntPtr hProv, uint dwFlags);
 
                [DllImport("CoreDll.dll")] 
                public static extern bool CryptGenRandom(
                    IntPtr hProv, int dwLen, byte[] pbBuffer);
            }
 
            // all static methods
            private PocketGuid()
            {
            }
 
            /// <summary>
            /// Return a new System.Guid object.
            /// </summary>
            public static Guid NewGuid()
            {
                IntPtr hCryptProv = IntPtr.Zero;
                Guid guid = Guid.Empty;
 
                try
                {
                    // holds random bits for guid
                    byte[] bits = new byte[Const.ByteArraySize];
 
                    // get crypto provider handle
                    if (!WinApi.CryptAcquireContext(ref hCryptProv, null, null, 
                        WinApi.PROV_RSA_FULL, WinApi.CRYPT_VERIFYCONTEXT))
                    {
                        throw new SystemException(
                            "Failed to acquire cryptography handle.");
                    }
 
                    // generate a 128 bit (16 byte) cryptographically random number
                    if (!WinApi.CryptGenRandom(hCryptProv, bits.Length, bits))
                    {
                        throw new SystemException(
                            "Failed to generate cryptography random bytes.");
                    }
 
                    // set the variant
                    bits[Const.VariantByte] &= Const.VariantByteMask;
                    bits[Const.VariantByte] |= 
                        ((int)GuidVariant.Standard << Const.VariantByteShift);
 
                    // set the version
                    bits[Const.VersionByte] &= Const.VersionByteMask;
                    bits[Const.VersionByte] |= 
                        ((int)GuidVersion.Random << Const.VersionByteShift);
 
                    // create the new System.Guid object
                    guid = new Guid(bits);
                }
                finally
                {
                    // release the crypto provider handle
                    if (hCryptProv != IntPtr.Zero)
                        WinApi.CryptReleaseContext(hCryptProv, 0);
                }
 
                return guid;
            }
        }
#endif
        #endregion Compact Framework Helper Classes
    }
}