Auto-generate Unique ID within the constructor - vb.net

Using VB.net, create a new class called staff, with three properties:
Name , LastName, ID - should be suitable for use as a primary key in a database.
Provide a class constructor to populate Name and LastName. ID should be auto-generated within the constructor and should not be passed in.
I know how to create a class, properties and constructor, I just need to know how to auto-generate ID field within the constructor. Is it possible to do this?
what I usually do is either make the id field in database as identity field and primary key so it automatically inserts the next available id or In my application I read the last ID from database and add one to it. But I need to know how to auto-generate ID field within the constructor.

Guid
If you do not have any constrain about ID type you can use a GUID:
Dim id As Guid = Guid.NewGuid()
You may even keep it as string:
Dim id As String = Guid.NewGuid().ToString("N")
That should be granted to be unique across different machines (to satisfy your requirement that it has to be suitable for use as a primary key in a database). See also this post.
Timestamp
That was worse case, if you do not have such strict requirement (uniqueness across a network) you may use a timestamp. Of course, in this case, you have to consider more issues:
Legal time: time goes back and forward twice per year.
Zones: what if user enter data in London and then he moves to New York?
Concurrency: you have to assume no one else adds records to your database (you may have collisions if they use a different technique). Also you can't apply this if execution is concurrent (multiple instance of your program running together).
Timer granularity: system date has a coarse granularity: if you construct many objects in a short period of time then you may have duplicate IDs. Workaround in this post.
Counter
If all these conditions are satisfied:
Multiple instances of your application won't run in parallel.
You're working on a single computer (not a network).
Database is empty every time your application starts.
You may use a Shared counter incremented each time a new object is constructed. If system timer granularity isn't an issue (see paragraph about timestamp)you may use system up time as ID. With limitations about granularity it should work even with multiple instances of the same application running concurrently.
If you use a Shared field you have to deal with synchronization issues. As suggested in this comment you may use a SyncLock. As alternative you may use an Interlocked.Increment operation.
Hash code
If all condistions for a counter are satisfied and this one also:
Your application is 32 bit.
Your object is not a ValueType and it doesn't override GetHashCode() method.
You can use hash-code (obtained with GetHashCode()) because (from MSDN):
In other words, two objects for which the ReferenceEquals method returns true have identical hash codes.
Because Object.ReferenceEquals() returns true only if you compare same instance then each instance will have a unique hash code (because in a 32 bit application hash code is object reference itself). Be aware this is an implementation detail and it may change.
Random number
I know this may shock someone but a good random number generator for a 64 bit value has a very low probability of collisions. I repeat very very low probability (see this article for more math details). Just do not use System.Random for this!
According to which seed you use you may be able to generate random numbers in a network scenario too (do not forget - citation needed - that earlier drafts for one local network protocol proposed a 32 bit random number as address, it has been changed because of bad feedback from users but it doesn't mean it can't work).

You want a number that won't repeat ever! So why not just use this?
Dim dateAndTime As Date
dateAndTime = Now
TextBoxPID.Text = Format(dateAndTime, "yyyyMMddHHmmss").ToString
Unless your data entries are going to take place in milliseconds, this solution works great. If you are running into a millisecond issue then just add a counter to the end of the string.
counter +=1
TextBoxPID.Text = Format(dateAndTime, "yyyyMMddHHmmss").ToString & counter.ToString
If you are working on a network and have several people doing data entry then add their employee id to the string. There are easy solution to every issue, but in most, if not all cases, this will work without issue.

Generate Random Unique User ID depending on SNTP server.
My requirements are a bit different; yet I needed to generate a random and unique User ID, that is 10 numbers, spending fair time couldn't find a suitable solution.
so I ended up with the following function; its unique and random result.
As per one application instant on one test machine it is incremental unique result; because the user will generate the ten digits one time on a non pre-selective timestamp. In addition to playing with the random alpha prefix; I hope this function can provide a solution:
Imports System.Globalization
Imports System.Net
Public Class GetNetTime
Public Shared Function GetUTC()
' connect to google servers
' you could use some SNTP time servers but can't be sure port will be open
' or you could just ping your own webserver
Dim myNetRequest As WebRequest = HttpWebRequest.Create("http://www.example.com")
' read response header from connection
Dim response = myNetRequest.GetResponse()
' read date/time header
' assume its UTC format
Dim GlobalUTC As String = response.Headers("date").ToString
' convert string to datetime object
Dim parsedDateTime As DateTime = DateTime.Parse(GlobalUTC)
' get UNIX time stamp
Dim unixTime = (parsedDateTime - New DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds
Return unixTime
End Function
End Class
To test the output, you could add:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim utc As String = GetNetTime.GetUTC
' add random alpha prefix to UNIX time stamp
Dim sPrefix As String = ""
Dim rdm As New Random()
For i As Integer = 1 To 3 ' if you need more than 3 alpah random charachters adjust i length
sPrefix &= ChrW(rdm.Next(65, 90))
Next
MsgBox(sPrefix & utc) ' OR MsgBox("ID" & sPrefix & utc)
' code here to use result
End Sub
I find this solution more useful than querying the SQL table and read last record id and do increment.
Notes:
Please don't mind long answer; as I tried to comment the code and
explain the scenario in details.
I think this is good for generating
UserID for application running on multiple workstations.
Please don't put the function in for ... loop or exhaust run it.
Output examples:
GYK1501270543
VWT1501270606
WRH1501270634
SKI1501270648
QXL1501270716

This is also based on #wpcoder answers above but a basic form and this one works for me
Public Function UIDGen(ByRef f As String) As String
Dim currentTime As DateTime = DateTime.UtcNow
Dim StringTime As String = currentTime.ToString
Dim parsedDateTime As DateTime = DateTime.Parse(StringTime)
Dim unixTime = (parsedDateTime - New DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds
Dim utcString As String = unixTime.ToString
Dim sPrefix As String = ""
Dim rdm As New Random()
For i As Integer = 1 To 3 ' 3 Letters enough ?
sPrefix &= ChrW(rdm.Next(65, 90))
Next
f = (sPrefix & utcString)
Return f
End Function

Related

i want to make a new variable that says shot1 shot2 shot3 so on and forth how do i do this?

Here is what i have tried so far , but no new shot variable is being declared
Module Module1
Dim shotlist As New List(Of Boolean)
Dim shono As Integer = 0
Dim shonos As String
Dim shotname As String
Dim fshot As Boolean
Dim shots As String
Sub Main()
For i As Integer = 0 To 1000
Dim ("shots" & i) as String = "shots" & i
fshot = Convert.ToBoolean(shots)
Next
End Sub
End Module
You can't do things this way. The variable names you see when you write a program are lost, turned into memory addresses by the compiler when it compiles your program. You cannot store any information using a variable's name - the name is just a reference for you, the programmer, during the time you write a program (design time)
Think of variables like buckets, with labels on the outside. Buckets only hold certain kinds of data inside
Dim shotname as String
This declares a bucket labelled shotname on the outside and it can hold a string inside. You can put a string inside it:
shotname = "Shot 1"
You can put any string you like inside this bucket, and thus anything you can reasonably represent as a string can also be put inside this bucket:
shotname = DateTime.Now.ToString()
This takes the current time and turns it into a string that looks like a date/time, and puts it in the bucket. The thing in the bucket is always a string, and lots of things (nearly anything actually) can be represented as a string, but we don't type all our buckets as strings because it isn't very useful - if you have two buckets that hold numbers for example, you can multiply them:
Dim a as Integer
a=2
Dim b as Integer
b=3
Dim c as Integer
c=a*b
But you can't multiply strings, even if they're strings trust look like numbers; strings would have to be converted to numbers first. Storing everything as a string and converting it to some other type before working on it then converting it back to a string to store it would be very wearisome
So that's all well and good and solves the problem of storing varying information in the computer memory and giving it a name that you can reference it by, but it means the developer has to know all the info that will ever be entered into the program. shotname is just one bucket storing one bit of info. Sure you could have
Dim shotname1 as String
Dim shotname2 as String
Dim shotname3 as String
But it would be quite tedious copy paste exercise to do this for a thousand shotname, and after you've done it you have to refer to all of them individually using their full name. There isn't a way to dynamically refer to bucket labels when you write a program, so this won't work:
For i = 0 To 1000
shotname&i = "This is shotname number " & i
Next i
Remember, shotname is lost when compiling, and i is too, so shotname&i just flat out cannot work. Both these things become, in essence, memory addresses that mean something to the compiler and you can't join two memory addresses together to get a third memory address that stores some data. They were only ever nice names for you to help you understand how to write the program, pick good names and and not get confused about what is what
Instead you need the mechanism that was invented to allow varying amounts of data not known at the design-time of the program - arrays
At their heart, arrays are what you're trying to do. You're trying to have load of buckets with a name you can vary and form the name programmatically.
Array naming is simple; an array has a name like any other variable (what I've been calling a bucket up to now - going to switch to calling them variables because everyone else does) and the name refers to the array as a whole, but the array is divided up into multiple slots all of which store the same type of data and have a unique index. Though the name of the array itself is fixed, the index can be varied; together they form a reference to a slot within the array and provide a mechanism for having names that can be generated dynamically
Dim shotnames(999) as String
This here is an array of 1000 strings, and it's called shotnames. It's good to use a plural name because it helps remind you that it's an array, holding multiple something. The 999 defines the last valid slot in the array - arrays start at 0, so with this one running 0 to 999 means there are 1000 entries in it
And critically, though the shotnames part of the variable name remains fixed and must be what you use to refer to the array itself(if you want to do something with the entire thing, like pass it to a function), referring to an individual element/slot in the array is done by tacking a number onto the end within brackets:
shotnames(587) = "The 588th shotname"
Keep in mind that "starts at 0 thing"
This 587 can come from anything that supplies a number; it doesn't have to be hard coded by you when you write the program:
For i = 0 to 999
shotnames(i) = "The " & (i+1) & "th shotname"
Next i
Or things that generate numbers:
shotnames(DateTime.Now.Minute) = "X"
If it's 12:59 when this code runs, then shotnames(59), the sixtieth slot in the array, will become filled with X
There are other kinds of varying storage; a list or a dictionary are commonly used examples, but they follow these same notions - you'll get a variable where part of the name is fixed and part of the name is an index you can vary.
At their heart they are just arrays too- if you looked inside a List you'd find an array with some extra code surrounding it that, when the array gets full, it makes a new array of twice the size and copies everything over. This way it provides "an array that expands" type functionality - something that arrays don't do natively.
Dictionaries are worth mentioning because they provide a way to index by other things than a number and can achieve storage of a great number of things not known at design time as a result:
Dim x as New Dictionary(Of String, Object)
This creates a dictionary indexed by a string that stores objects (I.e. anything - dates, strings, numbers, people..)
You could do like:
x("name") = "John"
x("age") = 32
You could even realize what you were trying to do earlier:
For i = 0 to 999
x("shotname"&i) = "The " & (i+1) & "th shotname"
Next i
(Though you'd probably do this in a Dictionary(Of string, string), ie a dictionary that is both indexed by string and stores strings.. and you probably wouldn't go to this level of effort to have a dictionary that stores x("shotname587") when it's simpler to declare an array shotname(587))
But the original premise remains true: your variable has a fixed part of the name (ie x) and a changeable part of the name (ie the string in the brackets) that is used as an index.
And there is no magic to the indexing by string either. If you opened up a dictionary and looked inside it, you'd find an array, together with some bits of code that take the string you passed in as an index, and turn it into a number like 587, and store the info you want in index 587. And there is a routine to deal with the case where two different strings both become 587 when converted, but other than that it's all just "if you want a variable where part of the name is changeable/formable programmatically rather than by the developer, it's an array"

What is the best way to compare two product / assembly versions

Let us say that a product has an assembly version number of 1.5.4537.56784. There are plenty of examples of how to retrieve this on SO and in most cases (if you simply want to display the information to the end user) then a default string representation is fine.
I now find myself wanting to compare this assembly number with another (this time most definitely stored as a string as it's in a text file online (but in the same format)) to determine whether the version number stored online is the same as the version number retrieved from the executing assembly or it is in fact greater. The term greater is important, not just different but actively greater.
A simple string compare ought to determine if they are the same but I don't think it would determine greater than. So what type ought I consider changing these stings to in order to make the greater than comparison. Or have I missed something obvious with string comparisons.
Split your values by . into an Integer array or list, whichever you prefer. Then use a simple If x > y comparison for each position in the collection.
Dim myAssemblyVersion = "1.5.4537.56784"
Dim theirAssemblyVersion = "1.5.4827.1"
Dim myIntegers = myAssemblyVersion.Split(".")
Dim theirIntegers = theirAssemblyVersion.Split(".")
Dim isTheirsgreater As Boolean = False
For i = 0 To myIntegers.Count - 1
If theirIntegers(i) > myIntegers(i) Then
isTheirsgreater = True
End If
Next
I would say the best way to compare versions is to use the comparison operators provided by the System.Version class. You can create a new instance of Version using the string you found in the text file online. Then you can use the GreaterThan operator to compare it to the version of your application.
Dim textVer As String = "1.5.4537.56784"
Dim onlineVer As New Version(textVer)
Dim myVer As Version = Reflection.Assembly.GetExecutingAssembly.GetName.Version
If onlineVer > myVer Then
'Version found online is greater than mine
End If

Make pc specific hash in vb.net

I want to make a program, which generate the same string each time, and it must be different on any each pc. So like HWID. After I have the string I send it into a php file on a remote host, the php handle it, and store it in the database.
On the first run it will make a new row in the table, but after 2nd run, it will select the row where the POST-ed hash = the hash in the table, and it has banned - not banned function. So if I give back 0 the pc is not banned, so program start to run, if I give back 1 the program close.
This is all made, my problem is, I generate hwid from processorid, and send it to the php. the processorid can be the same on different computers sometimes. So if I give fake ban, the users will be angry for me...
The question is:
How to generate a hash, which will be always the same on the pc which run the application, but different on each pc?
I know I can make it if I store a special id on the pc for example in the registry, but if somebody reinstall the pc, he can use again the service. If I generate hwid, it will takes him more time to find out how to access again to the service.
I dont think this really has anything to do with PHP, but entirely about the client side steps.
To do what it sounds like you want, you want to use a hardware signature made up of several things so that if one or two are unavailable, the result is still valid. This will use a form of the WMI polling procedure from the answer on your last question:
Private Shared Function GetHardwareItemInfo(item As String, wmiclass As String) As String
Dim data As String = ""
Dim query As String = String.Format("Select {0} From {1}", item, wmiclass)
Using mbs As ManagementObjectSearcher = New ManagementObjectSearcher(query)
For Each mo As ManagementObject In mbs.Get
For Each pd As PropertyData In mo.Properties
' should be only one
If String.Compare(pd.Name, item, StringComparison.InvariantCultureIgnoreCase) = 0 Then
' value is object, test for Nothing
If pd.Value IsNot Nothing Then
data = pd.Value.ToString
End If
Exit For
End If
Next
Next
End Using
Return data
End Function
This allows you to poll for different items in different wmi classes using the same code. Example:
' get the serialnumber item from the baseboard class:
answer = GetHardwareItemInfo("serialNumber", "Win32_BaseBoard")
For a hardware signature:
Get and store the info for each item
Combine them into one string
Convert the string to a byte array
Use crypto to hash the byte array
convert the result to a base64 string
There are other ways. For instance you could encode the result as a Hex string, but the above is what the code shows. First, these are the namespaces you need:
Imports System.Security.Cryptography
Imports System.Management
Imports System.Text
Then the procedure to get the stuff using the GetHardwareItemInfo method above:
' place to store bits of data
Dim HWStuff As New List(Of String)
Dim answer As String
' get and store some info
answer = GetHardwareItemInfo("serialNumber", "Win32_BaseBoard")
HWStuff.Add(answer)
answer = GetHardwareItemInfo("uuid", "win32_ComputerSystemProduct")
HWStuff.Add(answer)
answer = GetHardwareItemInfo("serialNumber", "Win32_OperatingSystem")
HWStuff.Add(answer)
'...etc
' glue the bits together into one string
Dim HWSig = String.Join("", HWStuff)
Dim byteHash As Byte()
' create crypto hasher
Using hasher = New SHA1Managed()
' convert the string to bytes
Dim tmpBytes = Encoding.UTF8.GetBytes(HWSig)
'hash the bytes
byteHash = hasher.ComputeHash(tmpBytes)
End Using
' encode as B64 string.
Dim HWHash = Convert.ToBase64String(byteHash)
Console.WriteLine(HWHash)
Result:
MUjeLeZtbTQ3Rc8zgFquBkOwFzA=
You could glue the string together as you get answers. But during development it helps to see the candidate info before you decide to use it or not.
Notes:
There are many many things to choose from. See WMI Win32 Classes.
Not everything needs to come from WMI. the LocalMachine name might be a good one (I have no idea of the context for this) as is the Windows Activation Key.
Other crypto hashers will produce longer hashes
This is far from foolproof.
Some things can be spoofed - the Win OS Serial number can be changed in the registry. You dont really care if the values are right, just that they do not change.
This is not copy protection. Someone could sniff out the token(s) sent from a legitimate system(s), then patch your app to send that token only.
if I store a special id...
No. Do not write anything down. Its impossible to keep a secret from the user on their own PC. Dont store the hash either - generate it every time. If you write it down it is easier to copy that value to a different machine.
I give fake ban, the users will be angry for me...
Since it sounds like you are working from a blacklist rather than a whitelist, you dont have to worry about the hash failing. The worst that will happen is that a system which should be denied access will get access. If you want to further reduce the chance of a match, use SHA512Managed; it will produce a longer hash though.
If a user changes one of the parts you are polling, they will still get in - it is quite unlikely that the hash from 2 systems will match (one white, one black).

Random from listbox

there has been many posts about this but nobody seems to have my problem.
I have a list box with letters a-z (line for each letter)
using
Dim rancon As New Random
Dim rc As Integer = ListBox1.Items.Count
Label5.Text = lcon.Items.Item(rancon.Next(rc)).ToString
all it is doing is chosing randomly, 1 of the first 7 or 8 characters.
can anyone advise?
I assume that you expect that the random always chooses a different item in the ListBox. Therefore you have to reuse the same Random instance since the default constructor derives the seed(which is used to initialize the pseudorandom number generator) from the current system time.
If you call this code very fast(for example in a loop), the seed will always be the same. Hence you get repeating numbers/items.
To avoid this you could make the Random a field in the class:
Private rancon As New Random
Public Sub YourMethod()
Dim rc As Integer = ListBox1.Items.Count
Label5.Text = ListBox1.Items(rancon.Next(rc)).ToString()
End Sub
MSDN:
The default seed value is derived from the system clock and has finite
resolution. As a result, different Random objects that are created in
close succession by a call to the default constructor will have
identical default seed values and, therefore, will produce identical
sets of random numbers. This problem can be avoided by using a single
Random object to generate all random numbers. You can also work around
it by modifying the seed value returned by the system clock and then
explicitly providing this new seed value to the Random(Int32)
constructor.
Note that i've also used ListBox1.Items instead of lcon.Items in case that was a typo.

Better socket communication system

Currently, the client is sending messages like this:
Public Function checkMD5(ByVal userID As Integer, ByVal gameID As Integer, ByVal file As String, ByVal fileFull As String) As String
Dim make As New CMakeMSG
Dim md5 As New CMD5
make.append("checkfileMD5")
make.append(userID)
make.append(containerID)
make.append(file)
make.append(md5.GenerateFileHash(fileFull))
Return SocketSendAndReceiveMSG(make.makestring)
End Function
The server may receive something like this:
checkfileMD5-MSGDelimit0-12-MSGDelimit1-54-MSGDelimit2-filename.txt-MSGDelimit3-*md5hash*
Which it then reads out:
Private _message As String
Public Function handleMessage() As String
Dim brokenMessage As New ArrayList
brokenMessage = breakDown() 'Split to ArrayList
If brokenMessage(0) = "checkfileMD5" Then
Try
If brokenMessage.Count > 5 Then
Return "0-structureMessedUp"
End If
Return CompareFileMD5(brokenMessage(1), brokenMessage(2), brokenMessage(3), brokenMessage(4))
Catch ex As Exception
Return "0-structureMessedUp"
End Try
End If
End Function
So what it does is take the received message, and split it to an array using the -MSGDelimit- as a delimiter. So in this case the CompareFileMD5() function would receive 12,54,filename.txt,*md5hash*. And based on that it can return to the client whether or not the MD5 matched.
Sure, it works, but it feels sloppy and code on the server gets really messy.
Here's the less relevant functions from the above code (doubt it matters, but you never know):
Private Function breakDown() As ArrayList
Try
Dim theArray As New ArrayList
Dim copymsg As String = _message
Dim counter As Integer = 0
Do Until Not copymsg.Contains("-MSGDelimit")
Dim found As String
found = copymsg.Substring(0, copymsg.IndexOf("-MSGDelimit" & counter & "-"))
theArray.Add(found)
copymsg = copymsg.Replace(found & "-MSGDelimit" & counter & "-", "")
counter += 1
Loop
theArray.Add(copymsg)
Return theArray
Catch ex As Exception
Module1.msg(ex.Message)
End Try
End Function
Private Function CompareFileMD5(ByVal userID As Integer, ByVal gameID As Integer, ByVal filename As String, ByVal source As String) As String
Try
Dim tryFindFile As String = Module1.filedatabase.findfile(userID, gameID, filename)
If Not tryFindFile = "notFound" Then
Dim fileFull As String = tryFindFile & "\" & filename
Dim md5 As New CMD5
If md5.GenerateFileHash(fileFull) = source Then
Return "Match"
Else
Return "NoMatch"
End If
Else
Return "notFound"
End If
Catch ex As Exception
Module1.msg("0")
Return "0"
End Try
End Function
So, any advice on how to handle it better/cleaner/more professional?
Depending on the application, your current solution may be perfectly fine. There are a couple of things that do stand out a little bit:
The "protocol" is a bit heavy in terms of the amount of data sent. The delimiters between the data pieces adds quite a bit of overhead. In the example, it makes up maybe 50% of the payload. In addition, sending all data as text potentially makes the payload larger than absolutely necessary. All of this, though, is not necessarily a problem. If the traffic between the client and server is relatively light, then the extra data on the wire may not be a problem at all. For a request of this size (with or without the relatively high delimiter overhead), the main cost will be round trip costs and would likely change very little by reducing the size of this packet by half. If, though, there are requests with thousands of pieces of data, then reducing the payload size would help.
The use of the delimiters as shown is potentially ambiguous depending on the data sent. It is unlikely given the length and format of the delimiters, but it's something to keep in mind if there ever exists the possibility of having actual data that "looks" like a delimiter.
Assuming that the example shown is one of many similar protocols, I would be inclined to go a different route. One possibility would be to bundle up the request as a JSON object. There are existing packages available to create and read JSON. One example is Json.NET. JSON has a well-defined structure, it is easy for a human to read and verify, and it can be expanded easily. And depending on the data that you send, it would probably a little more lightweight than the current format. And (maybe the part you are interested in), it would maybe seem more "professional".
A couple of additional things that I would do (personal opinion):
Possibly add a client version to the data being sent so the server will know if it "recognizes" the request. Start the client version at some value (e.g., 1). If there are updates to the protocol format (e.g., different data, different structure), change the version to 2 in that release of the software. Then the server can look at the version number to see if it recognizes it. If it is the first version of the server and sees version 2, then it can return an error indicating the server needs to be updated. This is not necessary if you can guarantee that the client and server releases are always matched (sometimes this is hard in practice).
Use an integer value for the request type instead of a string ('checkFileMD5'). If there are going to be a large number of request types, the server can dispatch the request a little more efficiently (maybe) based on an integer value.