From c5c95b2e9854d2f04585b3a11087970c1791065a Mon Sep 17 00:00:00 2001
From: Matthew Hertzfeld <>
Date: Mon, 2 Mar 2020 16:03:48 -0500
Subject: [PATCH] Added new project for .Net Compact Framework 3.5.
Cloned original GuerrillaNtp project and made nessisary changes.
New project was built using VS2008, latest version of VS compatible is CF.
---
GuerrillaNtp_CF/GuerrillaNtp_CF.csproj | 75 +++++
GuerrillaNtp_CF/NtpClient.cs | 144 +++++++++
GuerrillaNtp_CF/NtpException.cs | 26 ++
GuerrillaNtp_CF/NtpLeapIndicator.cs | 30 ++
GuerrillaNtp_CF/NtpMode.cs | 20 ++
GuerrillaNtp_CF/NtpPacket.cs | 357 +++++++++++++++++++++
GuerrillaNtp_CF/Properties/AssemblyInfo.cs | 35 ++
7 files changed, 687 insertions(+)
create mode 100644 GuerrillaNtp_CF/GuerrillaNtp_CF.csproj
create mode 100644 GuerrillaNtp_CF/NtpClient.cs
create mode 100644 GuerrillaNtp_CF/NtpException.cs
create mode 100644 GuerrillaNtp_CF/NtpLeapIndicator.cs
create mode 100644 GuerrillaNtp_CF/NtpMode.cs
create mode 100644 GuerrillaNtp_CF/NtpPacket.cs
create mode 100644 GuerrillaNtp_CF/Properties/AssemblyInfo.cs
diff --git a/GuerrillaNtp_CF/GuerrillaNtp_CF.csproj b/GuerrillaNtp_CF/GuerrillaNtp_CF.csproj
new file mode 100644
index 0000000..e64ada0
--- /dev/null
+++ b/GuerrillaNtp_CF/GuerrillaNtp_CF.csproj
@@ -0,0 +1,75 @@
+
+
+ Debug
+ AnyCPU
+ 9.0.30729
+ 2.0
+ {6A920E41-4597-4312-A5D0-43CC0DE70EBC}
+ Library
+ Properties
+ GuerrillaNtp
+ GuerrillaNtp_CF
+ {4D628B5B-2FBC-4AA6-8C16-197242AEB884};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ PocketPC
+ b2c48bd2-963d-4549-9169-1fa021dce484
+ 5.2
+ GuerrillaNtp_CF
+ v3.5
+ Windows Mobile 6 Professional SDK
+
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE;$(PlatformFamilyName)
+ true
+ true
+ prompt
+ 512
+ 4
+ Off
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE;$(PlatformFamilyName)
+ true
+ true
+ prompt
+ 512
+ 4
+ Off
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/GuerrillaNtp_CF/NtpClient.cs b/GuerrillaNtp_CF/NtpClient.cs
new file mode 100644
index 0000000..fd91a50
--- /dev/null
+++ b/GuerrillaNtp_CF/NtpClient.cs
@@ -0,0 +1,144 @@
+// Part of GuerrillaNtp: https://guerrillantp.machinezoo.com
+using System;
+using System.Net;
+using System.Net.Sockets;
+
+namespace GuerrillaNtp
+{
+ ///
+ /// Represents UDP socket used to communicate with RFC4330-compliant SNTP/NTP server.
+ ///
+ ///
+ ///
+ /// See project homepage for guidance on how to use GuerrillaNtp.
+ /// Most applications should just call
+ /// after instantiating this class. Method
+ /// can be used to obtain additional details stored in reply .
+ ///
+ ///
+ /// This class holds unmanaged resources (the socket) and callers are responsible
+ /// for calling when they are done,
+ /// perhaps by instantiating this class in using block.
+ ///
+ ///
+ /// It is application responsibility to be a good netizen,
+ /// which most importantly means using reasonable polling intervals
+ /// and exponential backoff when querying public NTP server.
+ ///
+ ///
+ public class NtpClient
+ {
+ readonly Socket socket;
+
+ ///
+ /// Creates new from server endpoint.
+ ///
+ /// Endpoint of the remote SNTP server.
+ ///
+ ///
+ public NtpClient(IPEndPoint endpoint)
+ {
+ socket = new Socket(endpoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
+ try
+ {
+ socket.Connect(endpoint);
+ }
+ catch
+ {
+ throw;
+ }
+ }
+
+ ///
+ /// Creates new from server's IP address and optional port.
+ ///
+ /// IP address of remote SNTP server, standard NTP port is used.
+ ///
+ ///
+ public NtpClient(IPAddress address) : this(new IPEndPoint(address, 123)) { }
+
+ ///
+ /// Creates new from server's IP address and optional port.
+ ///
+ /// IP address of remote SNTP server
+ /// Port of remote SNTP server. Default is 123 (standard NTP port).
+ ///
+ ///
+ public NtpClient(IPAddress address, int port) : this(new IPEndPoint(address, port)) { }
+
+ ///
+ /// Queries the SNTP server and returns correction offset.
+ ///
+ ///
+ /// Use this method if you just want correction offset from the server.
+ /// Call to obtain
+ /// with additional information besides .
+ ///
+ ///
+ /// Offset that should be added to local time to match server time.
+ ///
+ /// Thrown when the server responds with invalid reply packet.
+ ///
+ /// Thrown when no reply is received before is reached
+ /// or when there is an error communicating with the server.
+ ///
+ ///
+ ///
+ public TimeSpan GetCorrectionOffset() { return Query().CorrectionOffset; }
+
+ ///
+ /// Queries the SNTP server with configurable request.
+ ///
+ /// SNTP request packet to use when querying the network time server.
+ /// SNTP reply packet returned by the server.
+ ///
+ /// constructor
+ /// creates valid request packet, which you can further customize.
+ /// If you don't need any customization of the request packet, call instead.
+ /// Returned contains correction offset in
+ /// property.
+ ///
+ ///
+ /// Thrown when the request packet is invalid or when the server responds with invalid reply packet.
+ ///
+ ///
+ /// Thrown when no reply is received before is reached
+ /// or when there is an error communicating with the server.
+ ///
+ ///
+ ///
+ ///
+ public NtpPacket Query(NtpPacket request)
+ {
+ request.ValidateRequest();
+ socket.Send(request.Bytes);
+ var response = new byte[160];
+ int received = socket.Receive(response);
+ var truncated = new byte[received];
+ Array.Copy(response, truncated, received);
+ NtpPacket reply = new NtpPacket(truncated) { DestinationTimestamp = DateTime.UtcNow };
+ reply.ValidateReply(request);
+ return reply;
+ }
+
+ ///
+ /// Queries the SNTP server with default options.
+ ///
+ ///
+ /// Use this method to obtain additional details from the returned
+ /// besides .
+ /// If you just need the correction offset, call instead.
+ /// You can customize request packed by calling .
+ ///
+ /// SNTP reply packet returned by the server.
+ /// Thrown when the server responds with invalid reply packet.
+ ///
+ /// Thrown when no reply is received before is reached
+ /// or when there is an error communicating with the server.
+ ///
+ ///
+ ///
+ ///
+ public NtpPacket Query() { return Query(new NtpPacket()); }
+ }
+}
diff --git a/GuerrillaNtp_CF/NtpException.cs b/GuerrillaNtp_CF/NtpException.cs
new file mode 100644
index 0000000..3bb6970
--- /dev/null
+++ b/GuerrillaNtp_CF/NtpException.cs
@@ -0,0 +1,26 @@
+// Part of GuerrillaNtp: https://guerrillantp.machinezoo.com
+using System;
+
+namespace GuerrillaNtp
+{
+ ///
+ /// Represents errors that occur in SNTP packets or during SNTP operation.
+ ///
+ public class NtpException : Exception
+ {
+ ///
+ /// Gets the SNTP packet that caused this exception, if any.
+ ///
+ ///
+ /// SNTP packet that caused this exception, usually reply packet,
+ /// or null if the error is not specific to any packet.
+ ///
+ public NtpPacket Packet { get; private set; }
+
+ internal NtpException(NtpPacket packet, String message)
+ : base(message)
+ {
+ Packet = packet;
+ }
+ }
+}
diff --git a/GuerrillaNtp_CF/NtpLeapIndicator.cs b/GuerrillaNtp_CF/NtpLeapIndicator.cs
new file mode 100644
index 0000000..8f0335e
--- /dev/null
+++ b/GuerrillaNtp_CF/NtpLeapIndicator.cs
@@ -0,0 +1,30 @@
+// Part of GuerrillaNtp: https://guerrillantp.machinezoo.com
+namespace GuerrillaNtp
+{
+ ///
+ /// Represents leap second warning from the server that instructs the client to add or remove leap second.
+ ///
+ ///
+ public enum NtpLeapIndicator
+ {
+ ///
+ /// No leap second warning. No action required.
+ ///
+ NoWarning,
+
+ ///
+ /// Warns the client that the last minute of the current day has 61 seconds.
+ ///
+ LastMinuteHas61Seconds,
+
+ ///
+ /// Warns the client that the last minute of the current day has 59 seconds.
+ ///
+ LastMinuteHas59Seconds,
+
+ ///
+ /// Special value indicating that the server clock is unsynchronized and the returned time is unreliable.
+ ///
+ AlarmCondition
+ }
+}
\ No newline at end of file
diff --git a/GuerrillaNtp_CF/NtpMode.cs b/GuerrillaNtp_CF/NtpMode.cs
new file mode 100644
index 0000000..0f0b116
--- /dev/null
+++ b/GuerrillaNtp_CF/NtpMode.cs
@@ -0,0 +1,20 @@
+// Part of GuerrillaNtp: https://guerrillantp.machinezoo.com
+namespace GuerrillaNtp
+{
+ ///
+ /// Describes SNTP packet mode, i.e. client or server.
+ ///
+ ///
+ public enum NtpMode
+ {
+ ///
+ /// Identifies client-to-server SNTP packet.
+ ///
+ Client = 3,
+
+ ///
+ /// Identifies server-to-client SNTP packet.
+ ///
+ Server = 4,
+ }
+}
\ No newline at end of file
diff --git a/GuerrillaNtp_CF/NtpPacket.cs b/GuerrillaNtp_CF/NtpPacket.cs
new file mode 100644
index 0000000..aa1aac8
--- /dev/null
+++ b/GuerrillaNtp_CF/NtpPacket.cs
@@ -0,0 +1,357 @@
+// Part of GuerrillaNtp: https://guerrillantp.machinezoo.com
+using System;
+
+namespace GuerrillaNtp
+{
+ ///
+ /// Represents RFC4330 SNTP packet used for communication to and from a network time server.
+ ///
+ ///
+ ///
+ /// See project homepage for guidance on how to use GuerrillaNtp.
+ /// Most applications should just use the property
+ /// or even better call .
+ ///
+ ///
+ /// The same data structure represents both request and reply packets.
+ /// Request and reply differ in which properties are set and to what values.
+ ///
+ ///
+ /// The only real property is .
+ /// All other properties read from and write to the underlying byte array
+ /// with the exception of ,
+ /// which is not part of the packet on network and it is instead set locally after receiving the packet.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public class NtpPacket
+ {
+ static readonly DateTime epoch = new DateTime(1900, 1, 1);
+
+ ///
+ /// Gets RFC4330-encoded SNTP packet.
+ ///
+ ///
+ /// Byte array containing RFC4330-encoded SNTP packet. It is at least 48 bytes long.
+ ///
+ ///
+ /// This is the only real property. All other properties except
+ /// read from or write to this byte array.
+ ///
+ public byte[] Bytes { get; private set; }
+
+ ///
+ /// Gets the leap second indicator.
+ ///
+ ///
+ /// Leap second warning, if any. Special value
+ /// indicates unsynchronized server clock.
+ /// Default is .
+ ///
+ ///
+ /// Only servers fill in this property. Clients can consult this property for possible leap second warning.
+ ///
+ public NtpLeapIndicator LeapIndicator
+ {
+ get { return (NtpLeapIndicator)((Bytes[0] & 0xC0) >> 6); }
+ }
+
+ ///
+ /// Gets or sets protocol version number.
+ ///
+ ///
+ /// SNTP protocol version. Default is 4, which is the latest version at the time of this writing.
+ ///
+ ///
+ /// In request packets, clients should leave this property at default value 4.
+ /// Servers usually reply with the same protocol version.
+ ///
+ public int VersionNumber
+ {
+ get { return (Bytes[0] & 0x38) >> 3; }
+ set { Bytes[0] = (byte)((Bytes[0] & ~0x38) | value << 3); }
+ }
+
+ ///
+ /// Gets or sets SNTP packet mode, i.e. whether this is client or server packet.
+ ///
+ ///
+ /// SNTP packet mode. Default is in newly created packets.
+ /// Server reply should have this property set to .
+ ///
+ public NtpMode Mode
+ {
+ get { return (NtpMode)(Bytes[0] & 0x07); }
+ set { Bytes[0] = (byte)((Bytes[0] & ~0x07) | (int)value); }
+ }
+
+ ///
+ /// Gets server's distance from the reference clock.
+ ///
+ ///
+ ///
+ /// Distance from the reference clock. This property is set only in server reply packets.
+ /// Servers connected directly to reference clock hardware set this property to 1.
+ /// Statum number is incremented by 1 on every hop down the NTP server hierarchy.
+ ///
+ ///
+ /// Special value 0 indicates that this packet is a Kiss-o'-Death message
+ /// with kiss code stored in .
+ ///
+ ///
+ public int Stratum { get { return Bytes[1]; } }
+
+ ///
+ /// Gets server's preferred polling interval.
+ ///
+ ///
+ /// Polling interval in log₂ seconds, e.g. 4 stands for 16s and 17 means 131,072s.
+ ///
+ public int Poll { get { return Bytes[2]; } }
+
+ ///
+ /// Gets the precision of server clock.
+ ///
+ ///
+ /// Clock precision in log₂ seconds, e.g. -20 for microsecond precision.
+ ///
+ public int Precision { get { return (sbyte)Bytes[3]; } }
+
+ ///
+ /// Gets the total round-trip delay from the server to the reference clock.
+ ///
+ ///
+ /// Round-trip delay to the reference clock. Normally a positive value smaller than one second.
+ ///
+ public TimeSpan RootDelay { get { return GetTimeSpan32(4); } }
+
+ ///
+ /// Gets the estimated error in time reported by the server.
+ ///
+ ///
+ /// Estimated error in time reported by the server. Normally a positive value smaller than one second.
+ ///
+ public TimeSpan RootDispersion { get { return GetTimeSpan32(8); } }
+
+ ///
+ /// Gets the ID of the time source used by the server or Kiss-o'-Death code sent by the server.
+ ///
+ ///
+ ///
+ /// ID of server's time source or Kiss-o'-Death code.
+ /// Purpose of this property depends on value of property.
+ ///
+ ///
+ /// Stratum 1 servers write here one of several special values that describe the kind of hardware clock they use.
+ ///
+ ///
+ /// Stratum 2 and lower servers set this property to IPv4 address of their upstream server.
+ /// If upstream server has IPv6 address, the address is hashed, because it doesn't fit in this property.
+ ///
+ ///
+ /// When server sets to special value 0,
+ /// this property contains so called kiss code that instructs the client to stop querying the server.
+ ///
+ ///
+ public uint ReferenceId { get { return GetUInt32BE(12); } }
+
+ ///
+ /// Gets or sets the time when the server clock was last set or corrected.
+ ///
+ ///
+ /// Time when the server clock was last set or corrected or null when not specified.
+ ///
+ ///
+ /// This Property is usually set only by servers. It usually lags server's current time by several minutes,
+ /// so don't use this property for time synchronization.
+ ///
+ public DateTime? ReferenceTimestamp { get { return GetDateTime64(16); } set { SetDateTime64(16, value); } }
+
+ ///
+ /// Gets or sets the time when the client sent its request.
+ ///
+ ///
+ /// This property is null in request packets.
+ /// In reply packets, it is the time when the client sent its request.
+ /// Servers copy this value from
+ /// that they find in received request packet.
+ ///
+ ///
+ ///
+ public DateTime? OriginTimestamp { get { return GetDateTime64(24); } set { SetDateTime64(24, value); } }
+
+ ///
+ /// Gets or sets the time when the request was received by the server.
+ ///
+ ///
+ /// This property is null in request packets.
+ /// In reply packets, it is the time when the server received client request.
+ ///
+ ///
+ ///
+ public DateTime? ReceiveTimestamp { get { return GetDateTime64(32); } set { SetDateTime64(32, value); } }
+
+ ///
+ /// Gets or sets the time when the packet was sent.
+ ///
+ ///
+ /// Time when the packet was sent. It should never be null.
+ /// Default value is .
+ ///
+ ///
+ /// This property must be set by both clients and servers.
+ ///
+ ///
+ ///
+ public DateTime? TransmitTimestamp { get { return GetDateTime64(40); } set { SetDateTime64(40, value); } }
+
+ ///
+ /// Gets or sets the time of reception of response SNTP packet on the client.
+ ///
+ ///
+ /// Time of reception of response SNTP packet on the client. It is null in request packets.
+ ///
+ ///
+ /// This property is not part of the protocol.
+ /// It is set by when reply packet is received.
+ ///
+ ///
+ ///
+ public DateTime? DestinationTimestamp { get; set; }
+
+ ///
+ /// Gets the round-trip time to the server.
+ ///
+ ///
+ /// Time the request spent travelling to the server plus the time the reply spent travelling back.
+ /// This is calculated from timestamps in the packet as (t1 - t0) + (t3 - t2)
+ /// where t0 is ,
+ /// t1 is ,
+ /// t2 is ,
+ /// and t3 is .
+ /// This property throws an exception in request packets.
+ ///
+ /// Thrown when one of the required timestamps is not present.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public TimeSpan RoundTripTime
+ {
+ get
+ {
+ CheckTimestamps();
+ return (ReceiveTimestamp.Value - OriginTimestamp.Value) + (DestinationTimestamp.Value - TransmitTimestamp.Value);
+ }
+ }
+
+ ///
+ /// Gets the offset that should be added to local time to synchronize it with server time.
+ ///
+ ///
+ /// Time difference between server and client. It should be added to local time to get server time.
+ /// It is calculated from timestamps in the packet as 0.5 * ((t1 - t0) - (t3 - t2))
+ /// where t0 is ,
+ /// t1 is ,
+ /// t2 is ,
+ /// and t3 is .
+ /// This property throws an exception in request packets.
+ ///
+ /// Thrown when one of the required timestamps is not present.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public TimeSpan CorrectionOffset
+ {
+ get
+ {
+ CheckTimestamps();
+ return TimeSpan.FromTicks(((ReceiveTimestamp.Value - OriginTimestamp.Value) - (DestinationTimestamp.Value - TransmitTimestamp.Value)).Ticks / 2);
+ }
+ }
+
+ ///
+ /// Initializes default request packet.
+ ///
+ ///
+ /// Created request packet can be passed to .
+ /// Properties and
+ /// are set appropriately for request packet. Property
+ /// is set to .
+ ///
+ ///
+ public NtpPacket()
+ : this(new byte[48])
+ {
+ Mode = NtpMode.Client;
+ VersionNumber = 4;
+ TransmitTimestamp = DateTime.UtcNow;
+ }
+
+ internal NtpPacket(byte[] bytes)
+ {
+ if (bytes.Length < 48)
+ throw new NtpException(null, "SNTP reply packet must be at least 48 bytes long.");
+ Bytes = bytes;
+ }
+
+ internal void ValidateRequest()
+ {
+ if (Mode != NtpMode.Client)
+ throw new NtpException(this, "This is not a request SNTP packet.");
+ if (VersionNumber == 0)
+ throw new NtpException(this, "Protocol version of the request is not specified.");
+ if (TransmitTimestamp == null)
+ throw new NtpException(this, "TransmitTimestamp must be set in request packet.");
+ }
+
+ internal void ValidateReply(NtpPacket request)
+ {
+ if (Mode != NtpMode.Server)
+ throw new NtpException(this, "This is not a reply SNTP packet.");
+ if (VersionNumber == 0)
+ throw new NtpException(this, "Protocol version of the reply is not specified.");
+ if (Stratum == 0)
+ throw new NtpException(this, String.Format("Received Kiss-o'-Death SNTP packet with code 0x{0:x}.", ReferenceId));
+ if (LeapIndicator == NtpLeapIndicator.AlarmCondition)
+ throw new NtpException(this, "SNTP server has unsynchronized clock.");
+ CheckTimestamps();
+ if (OriginTimestamp != request.TransmitTimestamp)
+ throw new NtpException(this, "Origin timestamp in reply doesn't match transmit timestamp in request.");
+ }
+
+ void CheckTimestamps()
+ {
+ if (OriginTimestamp == null)
+ throw new NtpException(this, "Origin timestamp is missing.");
+ if (ReceiveTimestamp == null)
+ throw new NtpException(this, "Receive timestamp is missing.");
+ if (TransmitTimestamp == null)
+ throw new NtpException(this, "Transmit timestamp is missing.");
+ if (DestinationTimestamp == null)
+ throw new NtpException(this, "Destination timestamp is missing.");
+ }
+
+ DateTime? GetDateTime64(int offset)
+ {
+ var field = GetUInt64BE(offset);
+ if (field == 0)
+ return null;
+ return new DateTime(epoch.Ticks + Convert.ToInt64(field * (1.0 / (1L << 32) * 10000000.0)));
+ }
+ void SetDateTime64(int offset, DateTime? value) { SetUInt64BE(offset, value == null ? 0 : Convert.ToUInt64((value.Value.Ticks - epoch.Ticks) * (0.0000001 * (1L << 32)))); }
+ TimeSpan GetTimeSpan32(int offset) { return TimeSpan.FromSeconds(GetInt32BE(offset) / (double)(1 << 16)); }
+ ulong GetUInt64BE(int offset) { return SwapEndianness(BitConverter.ToUInt64(Bytes, offset)); }
+ void SetUInt64BE(int offset, ulong value) { Array.Copy(BitConverter.GetBytes(SwapEndianness(value)), 0, Bytes, offset, 8); }
+ int GetInt32BE(int offset) { return (int)GetUInt32BE(offset); }
+ uint GetUInt32BE(int offset) { return SwapEndianness(BitConverter.ToUInt32(Bytes, offset)); }
+ static uint SwapEndianness(uint x) { return ((x & 0xff) << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | ((x & 0xff000000) >> 24); }
+ static ulong SwapEndianness(ulong x) { return ((ulong)SwapEndianness((uint)x) << 32) | SwapEndianness((uint)(x >> 32)); }
+ }
+}
diff --git a/GuerrillaNtp_CF/Properties/AssemblyInfo.cs b/GuerrillaNtp_CF/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..079fde3
--- /dev/null
+++ b/GuerrillaNtp_CF/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("GuerrillaNtp")]
+[assembly: AssemblyDescription("App-embedded NTP client for time-sensitive apps")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Robert Važan")]
+[assembly: AssemblyProduct("GuerrillaNtp")]
+[assembly: AssemblyCopyright("Copyright © 2020")]
+[assembly: AssemblyTrademark("Copyright © 2014-2020 Robert Važan")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("e59126d9-8544-41e5-8205-e385dc561133")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.4.1.0")]
+