Code development platform for open source projects from the European Union institutions

Skip to content
Snippets Groups Projects
Commit e5e29a33 authored by Kostis ANAGNOSTOPOULOS's avatar Kostis ANAGNOSTOPOULOS
Browse files

json: rework IO code.

* Bump ver 2.0.1-pre0-->pre1.
* Start logging stack-traces in the logs.
* REVERT null-setting trick for WorkingDir json-value.
* FIX SemanticVersion prerelease comparison.
parent 0954123f
No related branches found
No related tags found
No related merge requests found
...@@ -2,21 +2,32 @@ ...@@ -2,21 +2,32 @@
Imports Newtonsoft.Json.Linq Imports Newtonsoft.Json.Linq
Imports Newtonsoft.Json.Schema Imports Newtonsoft.Json.Schema
Imports System.Globalization
''' <summary>The parent-class for Head/Body json files</summary>
''' <remarks>The /Header/Strict boolean controles whether to allow additional-properties in Body, ''' <summary>The parent-class for Head/Body json files.
''' so it can be used to debug bad input-files by manually setting it to 'true' with a text-editor. '''
''' It is responsible for maintaining a "decent" Header by overlaying infos from subclasses,
''' and delegates the Body-building and its validation almost entirely to sub-classes.
''' </summary>
''' <remarks>
''' The /Header/Strict boolean controls whether to allow additional-properties in Body,
''' so it can be used to debug input-files by manually setting it to 'true' with a text-editor.
''' </remarks> ''' </remarks>
Public MustInherit Class cJsonFile Public MustInherit Class cJsonFile
Shared dateFrmt As String = "yyyy/MM/dd HH:mm:ss zzz"
''' <summary>The json-content for a json-file structured in Header/Body</summary> ''' <summary>The json-content for a json-file structured in Header/Body</summary>
Protected Shared Function JsonStr_FileContents(ByVal version As String, ByVal body As String) As String ''' <remarks>Note that the content is invalid according to the schema, and has to be specified by sub-classers.</remarks>
Protected Shared Function JsonStr_FileContents() As String
Return <json>{ Return <json>{
"Header": { "Header": {
"FileVersion": "<%= version %>", "Title": null,
"FileVersion": null,
"AppVersion": null,
"ModifiedDate": null,
"Strict": false, "Strict": false,
}, },
"Body": <%= body %> "Body": {}
}</json>.Value }</json>.Value
End Function End Function
...@@ -30,15 +41,28 @@ Public MustInherit Class cJsonFile ...@@ -30,15 +41,28 @@ Public MustInherit Class cJsonFile
"type": "object", "additionalProperties": true, "type": "object", "additionalProperties": true,
"required": true, "required": true,
"properties": { "properties": {
"Title": {
"type": "string",
},
"FileVersion": { "FileVersion": {
"type": "string", "type": "string",
"required": true, "required": true,
}, },
"AppVersion": {
"type": "string",
"required": true,
},
"ModifiedDate": {
"type": "string",
"required": true,
"description": "Last modified date",
},
"Strict": { "Strict": {
"title": "Validate body strictly",
"type": "boolean", "type": "boolean",
"required": true, "required": false,
"default": false, "description": "When True, the 'Body' does not accept unknown properties.",
} },
} }
}, },
"Body": {} "Body": {}
...@@ -46,11 +70,13 @@ Public MustInherit Class cJsonFile ...@@ -46,11 +70,13 @@ Public MustInherit Class cJsonFile
}</json>.Value }</json>.Value
End Function End Function
''' <summary>When a new file is created, it gets it /Header/FileVersion from this method</summary> ''' <summary>When a new file is Created or Stored, the contents return from this method is overlayed on /Header/*</summary>
Protected MustOverride ReadOnly Property CodeVersion() As String ''' <remarks>The result json must be valid overlaying this header.</remarks>
Protected MustOverride ReadOnly Property HeaderOverlay() As JObject
''' <summary>When a instance_with_defauls is created, it gets its /Body from this method</summary> ''' <summary>When a instance_with_defauls is Created, it gets its /Body from this method</summary>
Protected MustOverride ReadOnly Property CodeBodyStr() As String ''' <remarks>The result json must be valid after replacing with this body.</remarks>
Protected MustOverride ReadOnly Property BodyContent() As JObject
''' <summary>The schema used to validate the /Body</summary> ''' <summary>The schema used to validate the /Body</summary>
''' <remarks>To signify validation-failure it can throw an exception or add err-messages into the supplied list</remarks> ''' <remarks>To signify validation-failure it can throw an exception or add err-messages into the supplied list</remarks>
...@@ -69,9 +95,13 @@ Public MustInherit Class cJsonFile ...@@ -69,9 +95,13 @@ Public MustInherit Class cJsonFile
''' <remarks></remarks> ''' <remarks></remarks>
Protected Sub New(Optional ByVal inputFilePath As String = Nothing, Optional ByVal skipValidation As Boolean = False) Protected Sub New(Optional ByVal inputFilePath As String = Nothing, Optional ByVal skipValidation As Boolean = False)
If (inputFilePath Is Nothing) Then If (inputFilePath Is Nothing) Then
Dim jstr = JsonStr_FileContents(Me.CodeVersion, Me.CodeBodyStr) Dim jstr = JsonStr_FileContents()
Me.Json_Contents = JObject.Parse(jstr) Me.Json_Contents = JObject.Parse(jstr)
UpdateHeader()
Me.Json_Contents("Body") = Me.BodyContent
Else Else
fInfWarErrBW(5, False, format("Reading JSON-file({0})...", inputFilePath))
Me.Json_Contents = ReadJsonFile(inputFilePath) Me.Json_Contents = ReadJsonFile(inputFilePath)
End If End If
...@@ -80,8 +110,24 @@ Public MustInherit Class cJsonFile ...@@ -80,8 +110,24 @@ Public MustInherit Class cJsonFile
End If End If
End Sub End Sub
Protected Sub UpdateHeader()
Dim h As JObject = Me.Header
h("ModifiedDate") = DateTime.Now.ToString(dateFrmt)
h("AppVersion") = AppVers
If h("Strict") is Nothing then
h("Strict") = False
End If
For Each child As KeyValuePair(Of String, JToken) In Me.HeaderOverlay
h(child.Key) = child.Value
Next
End Sub
''' <summary>Validates and Writing to the config file</summary> ''' <summary>Validates and Writing to the config file</summary>
Sub Store(ByVal fpath As String) Sub Store(ByVal fpath As String)
UpdateHeader()
Validate(Me.Strict) Validate(Me.Strict)
WriteJsonFile(fpath, Json_Contents) WriteJsonFile(fpath, Json_Contents)
End Sub End Sub
...@@ -90,15 +136,19 @@ Public MustInherit Class cJsonFile ...@@ -90,15 +136,19 @@ Public MustInherit Class cJsonFile
''' <exception cref="SystemException">includes all validation errors</exception> ''' <exception cref="SystemException">includes all validation errors</exception>
''' <param name="isStrict">when True, no additional json-properties allowed in the data, when nothing, use value from Header</param> ''' <param name="isStrict">when True, no additional json-properties allowed in the data, when nothing, use value from Header</param>
Friend Sub Validate(Optional ByVal isStrict As Boolean? = Nothing) Friend Sub Validate(Optional ByVal isStrict As Boolean? = Nothing)
Dim allowsAdditionalProps As Boolean = Not IIf(isStrict Is Nothing, Me.Strict, isStrict)
Dim fileSchema = JsonSchema.Parse(JSchemaStr_File()) ' TODO: Lazily create schemas once
Dim validateMsgs As IList(Of String) = New List(Of String) Dim validateMsgs As IList(Of String) = New List(Of String)
Dim fileSchema = JsonSchema.Parse(JSchemaStr_File()) ' TODO: Lazily create this schema once
'' Validate Header
''
ValidateJson(Me.Json_Contents, fileSchema, validateMsgs) ValidateJson(Me.Json_Contents, fileSchema, validateMsgs)
If (validateMsgs.Any()) Then If (validateMsgs.Any()) Then
Throw New SystemException(format("Invalid File-format due to: {0}", String.Join(vbCrLf, validateMsgs))) Throw New SystemException(format("Invalid File-format due to: {0}", String.Join(vbCrLf, validateMsgs)))
End If End If
'' Validate Body
''
Dim allowsAdditionalProps As Boolean = Not IIf(isStrict Is Nothing, Me.Strict, isStrict)
Me.ValidateVersionAndBody(Me.FileVersion, Me.Body, allowsAdditionalProps, validateMsgs) Me.ValidateVersionAndBody(Me.FileVersion, Me.Body, allowsAdditionalProps, validateMsgs)
If (validateMsgs.Any()) Then If (validateMsgs.Any()) Then
...@@ -128,11 +178,29 @@ Public MustInherit Class cJsonFile ...@@ -128,11 +178,29 @@ Public MustInherit Class cJsonFile
Return Me.Json_Contents("Body") Return Me.Json_Contents("Body")
End Get End Get
End Property End Property
Public ReadOnly Property Title As String
Get
Return Me.Header("Title")
End Get
End Property
Public ReadOnly Property FileVersion As String Public ReadOnly Property FileVersion As String
Get Get
Return Me.Header("FileVersion") Return Me.Header("FileVersion")
End Get End Get
End Property End Property
Public ReadOnly Property AppVersion As String
Get
Return Me.Header("AppVersion")
End Get
End Property
Public ReadOnly Property ModifiedDate As String
Get
Return Me.Header("ModifiedDate")
End Get
End Property
Public ReadOnly Property Strict As Boolean Public ReadOnly Property Strict As Boolean
Get Get
Return Me.Header("Strict") Return Me.Header("Strict")
......
...@@ -6,22 +6,25 @@ Imports Newtonsoft.Json.Schema ...@@ -6,22 +6,25 @@ Imports Newtonsoft.Json.Schema
Public Class cPreferences Public Class cPreferences
Inherits cJsonFile Inherits cJsonFile
Protected Overrides ReadOnly Property CodeVersion() As String Protected Overrides ReadOnly Property HeaderOverlay() As JObject
Get Get
Return "1.0.0" Return JObject.Parse(<json>{
"Title": "vecto-cse PREFERENCES",
"FileVersion": "1.0.0",
}</json>.Value)
End Get End Get
End Property End Property
' Default-prefs specified here. ' Default-prefs specified here.
Protected Overrides ReadOnly Property CodeBodyStr() As String Protected Overrides ReadOnly Property BodyContent() As JObject
Get Get
Return <json>{ Return JObject.Parse(<json>{
"WorkingDir": null, "WorkingDir": null,
"WriteLog": true, "WriteLog": true,
"LogSize": 2, "LogSize": 2,
"LogLevel": 5, "LogLevel": 5,
"Editor": "notepad.exe", "Editor": "notepad.exe",
}</json>.Value }</json>.Value)
End Get End Get
End Property End Property
...@@ -29,7 +32,7 @@ Public Class cPreferences ...@@ -29,7 +32,7 @@ Public Class cPreferences
Protected Function JSchemaStr(ByVal allowAdditionalProps As Boolean) As String Protected Function JSchemaStr(ByVal allowAdditionalProps As Boolean) As String
Dim allowAdditionalProps_str As String = IIf(allowAdditionalProps, "true", "false") Dim allowAdditionalProps_str As String = IIf(allowAdditionalProps, "true", "false")
Return <json>{ Return <json>{
"title": "cse-prefs.ver1.0", "title": "Schema for vecto-cse PREFERENCES",
"type": "object", "additionalProperties": <%= allowAdditionalProps_str %>, "type": "object", "additionalProperties": <%= allowAdditionalProps_str %>,
"required": true, "required": true,
"properties": { "properties": {
...@@ -75,26 +78,22 @@ Public Class cPreferences ...@@ -75,26 +78,22 @@ Public Class cPreferences
''' <exception cref="SystemException">includes all validation errors</exception> ''' <exception cref="SystemException">includes all validation errors</exception>
''' <param name="allowAdditionalProps">when False, no additional json-properties allowed in the data, when nothing, use value from Header</param> ''' <param name="allowAdditionalProps">when False, no additional json-properties allowed in the data, when nothing, use value from Header</param>
Protected Overrides Sub ValidateVersionAndBody(ByVal fileVersion As String, ByVal body As JObject, ByVal allowAdditionalProps As Boolean, ByVal validateMsgs As IList(Of String)) Protected Overrides Sub ValidateVersionAndBody(ByVal fileVersion As String, ByVal body As JObject, ByVal allowAdditionalProps As Boolean, ByVal validateMsgs As IList(Of String))
Dim fromVersion = "1.0.0" '' Check version
Dim toVersion = "2.0.0" ''
Dim fromVersion = "1.0.0--"
Dim toVersion = "2.0.0--" ' The earliest pre-release.
If Not IsSemanticVersionsSupported(fileVersion, fromVersion, toVersion) Then If Not IsSemanticVersionsSupported(fileVersion, fromVersion, toVersion) Then
validateMsgs.Add(format("Unsupported FileVersion({0}, was not in between [{1}, )", fileVersion, fromVersion, toVersion)) validateMsgs.Add(format("Unsupported FileVersion({0}, was not in between [{1}, {2})", fileVersion, fromVersion, toVersion))
Else Return
Dim schema = JsonSchema.Parse(JSchemaStr(allowAdditionalProps)) ' TODO: Lazily create schemas once
ValidateJson(body, schema, validateMsgs)
End If End If
End Sub
'' Check schema
''
Dim schema = JsonSchema.Parse(JSchemaStr(allowAdditionalProps)) ' TODO: Lazily create schemas once
ValidateJson(body, schema, validateMsgs)
End Sub
Public Overrides Function Equals(ByVal obj As Object) As Boolean
If obj Is Nothing OrElse Not Me.GetType().Equals(obj.GetType()) Then
Return False
Else
Return Me.Json_Contents.Equals(DirectCast(obj, cPreferences).Json_Contents)
End If
End Function
#Region "json props" #Region "json props"
...@@ -143,8 +142,13 @@ Public Class cPreferences ...@@ -143,8 +142,13 @@ Public Class cPreferences
End If End If
End If End If
Dim var As Object = value ' NOTE: Avoid early-binding so that Nulls do not end-up with String as schema-type. '' NOTE: Early-binding makes Nulls end-up as 'string' schema-type.
Me.Body("WorkingDir") = var ''
If value Is Nothing Then
Me.Json_Contents("Body")("WorkingDir") = Nothing
Else
Me.Json_Contents("Body")("WorkingDir") = value
End If
End Set End Set
End Property End Property
......
...@@ -58,7 +58,7 @@ Public NotInheritable Class cSemanticVersion ...@@ -58,7 +58,7 @@ Public NotInheritable Class cSemanticVersion
Private Shared ReadOnly AlphaRegex As New Regex("^[A-Za-z0-9\-\.]+$", RegexOptions.Compiled Or RegexOptions.Singleline) Private Shared ReadOnly AlphaRegex As New Regex("^[A-Za-z0-9\-\.]+$", RegexOptions.Compiled Or RegexOptions.Singleline)
''' <summary> ''' <summary>
''' The regular expression to use to parse a semantic version number. ''' The regular expression to use to parse a "strict" semantic version number.
''' </summary> ''' </summary>
Private Shared ReadOnly SemanticVersionRegex As New Regex( _ Private Shared ReadOnly SemanticVersionRegex As New Regex( _
"^(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)(-(?<prerelease>[A-Za-z0-9\-\.]+))?(\+(?<build>[A-Za-z0-9\-\.]+))?$", _ "^(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)(-(?<prerelease>[A-Za-z0-9\-\.]+))?(\+(?<build>[A-Za-z0-9\-\.]+))?$", _
...@@ -484,9 +484,9 @@ Public NotInheritable Class cSemanticVersion ...@@ -484,9 +484,9 @@ Public NotInheritable Class cSemanticVersion
Dim hasIdentifier1 = Not String.IsNullOrEmpty(identifier1) Dim hasIdentifier1 = Not String.IsNullOrEmpty(identifier1)
Dim hasIdentifier2 = Not String.IsNullOrEmpty(identifier2) Dim hasIdentifier2 = Not String.IsNullOrEmpty(identifier2)
If (hasIdentifier1 AndAlso Not hasIdentifier2) Then If (hasIdentifier1 AndAlso Not hasIdentifier2) Then
result = 1
ElseIf (Not hasIdentifier1 AndAlso hasIdentifier2) Then
result = -1 result = -1
ElseIf (Not hasIdentifier1 AndAlso hasIdentifier2) Then
result = 1
ElseIf (hasIdentifier1) Then ElseIf (hasIdentifier1) Then
Dim dotDelimiter As Char() = {"."c} Dim dotDelimiter As Char() = {"."c}
Dim parts1 = identifier1.Split(dotDelimiter, StringSplitOptions.RemoveEmptyEntries) Dim parts1 = identifier1.Split(dotDelimiter, StringSplitOptions.RemoveEmptyEntries)
......
...@@ -101,7 +101,8 @@ ...@@ -101,7 +101,8 @@
End Function End Function
' Generation or upgrade from the log file ' Generation or upgrade from the log file
Function fWriteLog(ByVal BegHinEnd As Integer, Optional ByVal InfWarErrEls As Integer = 4, Optional ByVal text As String = "") As Boolean Function fWriteLog(ByVal BegHinEnd As Integer, Optional ByVal InfWarErrEls As Integer = 4, Optional ByVal text As String = "", _
Optional ByVal ex As Exception = Nothing) As Boolean
' Style 1 ... Write beginning ' Style 1 ... Write beginning
' Style 2 ... Add ' Style 2 ... Add
' Style 3 ... Write end ' Style 3 ... Write end
...@@ -143,6 +144,10 @@ ...@@ -143,6 +144,10 @@
Case 4 ' Else Case 4 ' Else
FileOutLog.WriteLine(text) FileOutLog.WriteLine(text)
End Select End Select
If ex IsNot Nothing Then
FileOutLog.WriteLine(ex.StackTrace)
End If
FileOutLog.Close() FileOutLog.Close()
Case 3 ' At the end Case 3 ' At the end
FileOutLog.OpenWrite(LogFilenam, , True) FileOutLog.OpenWrite(LogFilenam, , True)
......
...@@ -30,7 +30,8 @@ Public Class F_Main ...@@ -30,7 +30,8 @@ Public Class F_Main
Try Try
AppPreferences = New cPreferences(PreferencesPath) AppPreferences = New cPreferences(PreferencesPath)
Catch ex As Exception Catch ex As Exception
fInfWarErr(9, False, format("Failed loading Preferences({0}) due to: {1}", PreferencesPath, ex.Message)) fInfWarErr(9, False, format("Failed loading Preferences({0}) due to: {1} \n\iThis is not a problem if it is the first time you launch the application.", _
PreferencesPath, ex.Message), ex)
configL = False configL = False
End Try End Try
...@@ -61,7 +62,7 @@ Public Class F_Main ...@@ -61,7 +62,7 @@ Public Class F_Main
AppPreferences.Store(PreferencesPath) AppPreferences.Store(PreferencesPath)
fInfWarErr(7, False, format("Stored new Preferences({0}).", PreferencesPath)) fInfWarErr(7, False, format("Stored new Preferences({0}).", PreferencesPath))
Catch ex As Exception Catch ex As Exception
fInfWarErr(9, False, format("Failed storing Preferences({0}) due to: {1}", PreferencesPath, ex.Message)) fInfWarErr(9, False, format("Failed storing Preferences({0}) due to: {1}", PreferencesPath, ex.Message), ex)
End Try End Try
End If End If
End Sub End Sub
...@@ -484,7 +485,7 @@ Public Class F_Main ...@@ -484,7 +485,7 @@ Public Class F_Main
Try Try
System.Diagnostics.Process.Start(manual_fname) System.Diagnostics.Process.Start(manual_fname)
Catch ex As Exception Catch ex As Exception
fInfWarErr(9, False, format("Failed opening User Manual({0}) due to: {1}", manual_fname, ex.Message)) fInfWarErr(9, False, format("Failed opening User Manual({0}) due to: {1}", manual_fname, ex.Message), ex)
End Try End Try
End Sub End Sub
#End Region #End Region
......
...@@ -54,7 +54,7 @@ Public Class F_Preferences ...@@ -54,7 +54,7 @@ Public Class F_Preferences
fInfWarErr(7, True, format("Preferences have changed.\n Do you want to restart VECTO now?")) fInfWarErr(7, True, format("Preferences have changed.\n Do you want to restart VECTO now?"))
Catch ex As Exception Catch ex As Exception
fInfWarErr(9, False, format("Failed storing Preferences({0}) due to: {1} \n Preferences left unmodified!", PreferencesPath, ex.Message)) fInfWarErr(9, False, format("Failed storing Preferences({0}) due to: {1} \n Preferences left unmodified!", PreferencesPath, ex.Message), ex)
End Try End Try
End If End If
......
...@@ -5,6 +5,17 @@ Imports System.Text.RegularExpressions ...@@ -5,6 +5,17 @@ Imports System.Text.RegularExpressions
Module Minor_routines Module Minor_routines
''' <summary>
''' Utility to check compatibility when reading files, ie 1.0.1-somePre is compatible with [1.0.0--, 2.0.0--)
''' </summary>
''' <param name="checkVersion">the version under investigation</param>
''' <param name="fromVersion">inclusive</param>
''' <param name="toVersion">exclusive</param>
''' <returns>true if fromVersion &lt;= checkVersion &lt; toVersion </returns>
''' <remarks>
''' All version-strings must be, syntactically, valid as Semantic-versions (see http://semver.org/).
''' Note that the earliest pre-release segment is the dash('-'), so 1.0.0-- is the earliest possible version from 1.x release train.
''' </remarks>
Function IsSemanticVersionsSupported(ByVal checkVersion As String, ByVal fromVersion As String, Optional ByVal toVersion As String = Nothing) As Boolean Function IsSemanticVersionsSupported(ByVal checkVersion As String, ByVal fromVersion As String, Optional ByVal toVersion As String = Nothing) As Boolean
Dim cver As New cSemanticVersion(checkVersion) Dim cver As New cSemanticVersion(checkVersion)
...@@ -152,7 +163,8 @@ Module Minor_routines ...@@ -152,7 +163,8 @@ Module Minor_routines
' Functions for the information depiction on the GUI with the backgroundworker (Info, Warning, Error) ' Functions for the information depiction on the GUI with the backgroundworker (Info, Warning, Error)
#Region " Communication functions" #Region " Communication functions"
' Output from Informations\Warnings\Errors on the GUI ' Output from Informations\Warnings\Errors on the GUI
Function fInfWarErr(ByVal Style As Integer, ByVal MsgBoxOut As Boolean, ByVal text As String) As Boolean Function fInfWarErr(ByVal Style As Integer, ByVal MsgBoxOut As Boolean, ByVal text As String, Optional ByVal ex As Exception = Nothing) As Boolean
' Declaration ' Declaration
Dim Styletext As String = "" Dim Styletext As String = ""
Dim StyleOut As String = "" Dim StyleOut As String = ""
...@@ -194,16 +206,16 @@ Module Minor_routines ...@@ -194,16 +206,16 @@ Module Minor_routines
Select Case Style Select Case Style
Case 0 To 7 ' Message Case 0 To 7 ' Message
F_Main.ListBoxMSG.Items.Add(text) F_Main.ListBoxMSG.Items.Add(text)
fWriteLog(2, 4, text) fWriteLog(2, 4, text, ex)
Case 8 ' Warning Case 8 ' Warning
F_Main.ListBoxWar.Items.Add(text) F_Main.ListBoxWar.Items.Add(text)
F_Main.TabPageWar.Text = Styletext & " (" & F_Main.ListBoxWar.Items.Count & ")" F_Main.TabPageWar.Text = Styletext & " (" & F_Main.ListBoxWar.Items.Count & ")"
fWriteLog(2, 2, text) fWriteLog(2, 2, text, ex)
Case 9 ' Error Case 9 ' Error
F_Main.ListBoxErr.Items.Add(text) F_Main.ListBoxErr.Items.Add(text)
F_Main.TabPageErr.Text = Styletext & " (" & F_Main.ListBoxErr.Items.Count & ")" F_Main.TabPageErr.Text = Styletext & " (" & F_Main.ListBoxErr.Items.Count & ")"
F_Main.TabControlOutMsg.SelectTab(2) F_Main.TabControlOutMsg.SelectTab(2)
fWriteLog(2, 3, text) fWriteLog(2, 3, text, ex)
End Select End Select
End If End If
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
' Description of the form ' Description of the form
Public Const AppName As String = "VECTO_CSE" ' Name of the programm Public Const AppName As String = "VECTO_CSE" ' Name of the programm
Public Const AppVers As String = "2.0.1-pre0" ' Version of the Programm Public Const AppVers As String = "2.0.1-pre1" ' Version of the Programm
Public AppDate As String ' Date of the compilation of the programm Public AppDate As String ' Date of the compilation of the programm
' Control variables ' Control variables
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment