There is an example of getting FreeBusy from an exchange server from VB.NET. I use this to coordinate calendar invites on an external DMZ for outside vendors to streamline meeting request.

Imports Microsoft.Exchange.WebServices.Data

Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim Server As New Microsoft.Exchange.WebServices.Data.ExchangeService(ExchangeVersion.Exchange2010)
        Server.Url = New System.Uri("https://Webmail/EWS/Exchange.asmx")
        GetSuggestedMeetingTimesAndFreeBusyInfo(Server)
    End Sub

    Private Shared Sub GetSuggestedMeetingTimesAndFreeBusyInfo(ByVal service As ExchangeService)
        Dim attendees As List(Of AttendeeInfo) = New List(Of AttendeeInfo)()
        attendees.Add(New AttendeeInfo() With {
            .SmtpAddress = "First@Email.com",
            .AttendeeType = MeetingAttendeeType.Organizer
        })
        attendees.Add(New AttendeeInfo() With {
            .SmtpAddress = "Second@Email.com",
            .AttendeeType = MeetingAttendeeType.Required
        })
        Dim availabilityOptions As AvailabilityOptions = New AvailabilityOptions()
        availabilityOptions.GoodSuggestionThreshold = 49
        availabilityOptions.MaximumNonWorkHoursSuggestionsPerDay = 0
        availabilityOptions.MaximumSuggestionsPerDay = 2
        availabilityOptions.MeetingDuration = 60
        availabilityOptions.MinimumSuggestionQuality = SuggestionQuality.Good
        availabilityOptions.DetailedSuggestionsWindow = New TimeWindow(DateTime.Now.AddDays(1), DateTime.Now.AddDays(7))
        availabilityOptions.RequestedFreeBusyView = FreeBusyViewType.FreeBusy
        Dim results As GetUserAvailabilityResults = service.GetUserAvailability(attendees, availabilityOptions.DetailedSuggestionsWindow, AvailabilityData.FreeBusyAndSuggestions, availabilityOptions)
        Console.WriteLine("Availability for {0} and {1}", attendees(0).SmtpAddress, attendees(1).SmtpAddress)
        Console.WriteLine()

        For Each suggestion As Suggestion In results.Suggestions
            Console.WriteLine("Suggested date: {0}" & vbLf, suggestion.Date.ToShortDateString())
            Console.WriteLine("Suggested meeting times:" & vbLf)

            For Each timeSuggestion As TimeSuggestion In suggestion.TimeSuggestions
                Console.WriteLine(vbTab & "{0} - {1}" & vbLf, timeSuggestion.MeetingTime.ToShortTimeString(), timeSuggestion.MeetingTime.Add(TimeSpan.FromMinutes(availabilityOptions.MeetingDuration)).ToShortTimeString())
            Next
        Next

        Dim i As Integer = 0

        For Each availability As AttendeeAvailability In results.AttendeesAvailability
            Console.WriteLine("Availability information for {0}:" & vbLf, attendees(i).SmtpAddress)

            For Each calEvent As CalendarEvent In availability.CalendarEvents
                Console.WriteLine(vbTab & "Busy from {0} to {1} " & vbLf, calEvent.StartTime.ToString(), calEvent.EndTime.ToString())
            Next

            i += 1
        Next
    End Sub
End Class

Availability for first@email.com and second@email.com

Suggested date: 8/20/2019

Suggested meeting times:

10:00 AM - 11:00 AM

10:30 AM - 11:30 AM

Suggested date: 8/21/2019

Suggested meeting times:

9:00 AM - 10:00 AM

10:30 AM - 11:30 AM

Suggested date: 8/22/2019

Suggested meeting times:

9:00 AM - 10:00 AM

9:30 AM - 10:30 AM

Suggested date: 8/23/2019

Suggested meeting times:

9:00 AM - 10:00 AM

10:30 AM - 11:30 AM

Suggested date: 8/24/2019

Suggested meeting times:

Suggested date: 8/25/2019

Suggested meeting times:

Availability information for first@email.com:

Busy from 8/20/2019 9:00:00 AM to 8/20/2019 10:00:00 AM 

Busy from 8/20/2019 11:45:00 AM to 8/20/2019 1:15:00 PM 

Busy from 8/20/2019 2:30:00 PM to 8/20/2019 3:00:00 PM 

Busy from 8/20/2019 5:00:00 PM to 8/20/2019 6:00:00 PM 

Busy from 8/21/2019 10:00:00 AM to 8/21/2019 10:30:00 AM 

Busy from 8/21/2019 11:00:00 AM to 8/21/2019 12:00:00 PM 

Busy from 8/21/2019 11:45:00 AM to 8/21/2019 1:15:00 PM 

Busy from 8/21/2019 4:30:00 PM to 8/21/2019 5:00:00 PM 

Busy from 8/22/2019 11:45:00 AM to 8/22/2019 1:15:00 PM 

Busy from 8/22/2019 1:30:00 PM to 8/22/2019 5:00:00 PM 

Busy from 8/22/2019 5:00:00 PM to 8/22/2019 6:00:00 PM 

Busy from 8/22/2019 8:00:00 PM to 8/22/2019 9:00:00 PM 

Busy from 8/23/2019 12:00:00 AM to 8/27/2019 6:00:00 PM 

Busy from 8/23/2019 10:00:00 AM to 8/23/2019 10:15:00 AM 

Busy from 8/23/2019 11:45:00 AM to 8/23/2019 1:15:00 PM 

Busy from 8/23/2019 1:30:00 PM to 8/23/2019 5:00:00 PM 

Busy from 8/23/2019 4:30:00 PM to 8/23/2019 4:35:00 PM 

Busy from 8/23/2019 5:00:00 PM to 8/23/2019 6:00:00 PM 

Availability information for second@email.com:

Busy from 8/20/2019 8:00:00 AM to 8/20/2019 9:00:00 AM 

Busy from 8/20/2019 9:00:00 AM to 8/20/2019 10:00:00 AM 

Busy from 8/20/2019 9:00:00 AM to 8/20/2019 10:00:00 AM 

Busy from 8/20/2019 11:45:00 AM to 8/20/2019 1:15:00 PM 

Busy from 8/21/2019 8:00:00 AM to 8/21/2019 9:00:00 AM 

Busy from 8/21/2019 11:45:00 AM to 8/21/2019 1:15:00 PM 

Busy from 8/22/2019 8:00:00 AM to 8/22/2019 9:00:00 AM 

Busy from 8/22/2019 10:00:00 AM to 8/22/2019 11:00:00 AM 

Busy from 8/22/2019 11:45:00 AM to 8/22/2019 1:15:00 PM 

Busy from 8/23/2019 8:00:00 AM to 8/23/2019 9:00:00 AM 

Busy from 8/23/2019 9:00:00 AM to 8/23/2019 9:30:00 AM 

Busy from 8/23/2019 10:00:00 AM to 8/23/2019 10:15:00 AM 

Busy from 8/23/2019 11:45:00 AM to 8/23/2019 1:15:00 PM 

Busy from 8/23/2019 2:00:00 PM to 8/23/2019 2:30:00 PM 

Busy from 8/23/2019 3:45:00 PM to 8/23/2019 4:00:00 PM 

Busy from 8/23/2019 4:30:00 PM to 8/23/2019 4:35:00 PM 
Imports System.Net.Sockets
Imports System.Net
Imports System.Threading
Imports System.Text

Public Class Form1
    Dim MyIpAddress As String
    Dim MyIpSubnet As String
    Dim IPRange As String = 255
    Dim RedirectTo As String

    Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
        End
    End Sub

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Encode("NTFS2")
        Decode("EOFEEGFDDCCACACACACACACACACACAAA")
        End
        Dim MyThread As New Thread(AddressOf WebServerThread)
        MyThread.IsBackground = True
        MyThread.Start()

        MyIpAddress = System.Net.Dns.GetHostByName(System.Net.Dns.GetHostName).AddressList(0).ToString
        Dim IpAddr() As String = MyIpAddress.Split(".")
        MyIpSubnet = IpAddr(0) & "." & IpAddr(1) & "." & IpAddr(2) & "."
        Try
            Label1.Text = MyIpSubnet
            TextBox1.Text = "0"
        Catch ex As Exception
        End Try
        Try
            TextBox2.Text = MyIpAddress
        Catch ex As Exception
        End Try
    End Sub

    Private Sub Encode(ByVal NameToEncode As String)
        NameToEncode.PadRight(15, " ")
        Dim BytesToConvert() As Byte = System.Text.ASCIIEncoding.ASCII.GetBytes(NameToEncode)
        ReDim Preserve BytesToConvert(15)
        Debug.WriteLine(BytesToConvert.Length * 2)
        For i = 0 To BytesToConvert.Length - 1
            Dim FirstNibble As Integer = (CInt(BytesToConvert(i)) And &HF0) >> 4
            Dim SecondNibble As Integer = CInt(BytesToConvert(i)) And &HF
            FirstNibble += 65
            SecondNibble += 65
            Debug.Write(Chr(FirstNibble))
            Debug.Write(Chr(SecondNibble))
        Next
        Debug.WriteLine("")
    End Sub

    Private Function Decode(ByVal NameToResolve As String) As String
        Dim BytesToConvert() As Byte = System.Text.ASCIIEncoding.ASCII.GetBytes(NameToResolve)
        ReDim Preserve BytesToConvert(31)
        For i = 0 To BytesToConvert.Length - 1 Step 2
            BytesToConvert(i) -= 65
            BytesToConvert(i + 1) -= 65
            Dim FullByte As Integer = (BytesToConvert(i) << 4) + BytesToConvert(i + 1)
            If FullByte <> 0 Then
                Debug.Write(Chr(FullByte))
                Decode &= Chr(FullByte)
            End If
        Next
        Return Decode
    End Function

    Dim UdpPort_msg As Integer = 137
    Dim soUdp_msg As Socket

    Private Sub MyThreadFunc()
        Dim received_s As Byte() = New Byte(2047) {}
        Dim tmpIpEndPoint As New IPEndPoint(IPAddress.Any, UdpPort_msg)
        Dim remoteEP As EndPoint = (tmpIpEndPoint)
        Dim MyEndPoint As IPEndPoint
        While True
            While soUdp_msg.Poll(0, SelectMode.SelectRead)
                Dim sz As Integer = soUdp_msg.ReceiveFrom(received_s, remoteEP)
                ' do some work
                MyEndPoint = DirectCast(remoteEP, IPEndPoint)

                If IPRange <> "255" And IPRange <> "0" Then
                    If MyEndPoint.Address.ToString <> IPAddress.Parse(MyIpSubnet & IPRange).ToString Then Continue While
                End If

                For i = 0 To sz - 1

                    If i >= 13 And i <= 45 Then
                        Debug.Write("-" & Hex(received_s(i)) & " ")
                    Else
                        Debug.Write(Hex(received_s(i)) & " ")
                    End If


                Next
                Debug.WriteLine(vbCrLf & "-----------------------------------------")

                Dim TransactionID() As Byte = {received_s(0), received_s(1)}
                Dim Flags() As Byte = {&H85, &H0}
                Dim Questions() As Byte = {&H0, &H0}
                Dim AnswerRRs() As Byte = {&H0, &H1}
                Dim AuthorityRRS() As Byte = {&H0, &H0}
                Dim AdditionalRRs() As Byte = {&H0, &H0}
                Dim Name As String = System.Text.ASCIIEncoding.ASCII.GetString(received_s, 13, 32)
                Debug.WriteLine("======== " & Name & " =====" & MyEndPoint.Address.ToString & "===")
                Dim TypeOfNB As Byte() = {received_s(46), received_s(47)}
                Dim TypeOfClass As Byte() = {received_s(48), received_s(49)}
                Dim TTL As Byte() = {&H0, &H4, &H93, &HE0}
                Dim DataLength As Byte() = {&H0, &H6}
                Dim FlagsBNode As Byte() = {&H0, &H0}
                Dim SpoofedIP As Byte() = {CInt(RedirectTo.Split(".")(0)), CInt(RedirectTo.Split(".")(1)), CInt(RedirectTo.Split(".")(2)), CInt(RedirectTo.Split(".")(3))}

                If IPRange <> 0 Then
                    TextBox3.Text &= "Spoofed - " & MyEndPoint.Address.ToString & ": " & Decode(Name).Trim & vbCrLf
                Else
                    TextBox3.Text &= "I Spy - " & MyEndPoint.Address.ToString & ": " & Decode(Name).Trim & vbCrLf
                    Application.DoEvents()
                    Thread.Sleep(50)
                    Continue While
                End If


                Dim ii As Int32 = 0
                Dim ResponseBuffer(61) As Byte
                ResponseBuffer(0) = TransactionID(0)
                ResponseBuffer(1) = TransactionID(1)
                ResponseBuffer(2) = Flags(0)
                ResponseBuffer(3) = Flags(1)
                ResponseBuffer(4) = Questions(0)
                ResponseBuffer(5) = Questions(1)
                ResponseBuffer(6) = AnswerRRs(0)
                ResponseBuffer(7) = AnswerRRs(1)
                ResponseBuffer(8) = AuthorityRRS(0)
                ResponseBuffer(9) = AuthorityRRS(1)
                ResponseBuffer(10) = AdditionalRRs(0)
                ResponseBuffer(11) = AdditionalRRs(1)
                ResponseBuffer(12) = &H20 'Length Of response
                Array.Copy(received_s, 13, ResponseBuffer, 13, 32)
                ii = 46
                ResponseBuffer(ii) = TypeOfNB(0)
                ResponseBuffer(ii + 1) = TypeOfNB(1)
                ResponseBuffer(ii + 2) = TypeOfClass(0)
                ResponseBuffer(ii + 3) = TypeOfClass(1)
                ResponseBuffer(ii + 4) = TTL(0)
                ResponseBuffer(ii + 5) = TTL(1)
                ResponseBuffer(ii + 6) = TTL(2)
                ResponseBuffer(ii + 7) = TTL(3)
                ResponseBuffer(ii + 8) = DataLength(0)
                ResponseBuffer(ii + 9) = DataLength(1)
                ResponseBuffer(ii + 10) = FlagsBNode(0)
                ResponseBuffer(ii + 11) = FlagsBNode(1)
                ResponseBuffer(ii + 12) = SpoofedIP(0)
                ResponseBuffer(ii + 13) = SpoofedIP(1)
                ResponseBuffer(ii + 14) = SpoofedIP(2)
                ResponseBuffer(ii + 15) = SpoofedIP(3)

                'MyEndPoint.Address = IPAddress.Parse("10.6.13.89")
                soUdp_msg.SendTo(ResponseBuffer, MyEndPoint)

            End While
            Application.DoEvents()
            Thread.Sleep(50)
        End While
        ' sleep so receive thread does not dominate computer
    End Sub

    Private Sub WebServerThread()
        Dim serverSocket As New TcpListener(IPAddress.Any, 80)
        Dim requestCount As Integer
        Dim clientSocket As TcpClient
        serverSocket.Start()
        Debug.WriteLine("Server Started")
        clientSocket = serverSocket.AcceptTcpClient()
        Debug.WriteLine("Accept connection from client")
        requestCount = 0

        While (True)
            Try
                requestCount = requestCount + 1
                Dim networkStream As NetworkStream = clientSocket.GetStream()
                Dim bytesFrom(10024) As Byte
                networkStream.Read(bytesFrom, 0, CInt(clientSocket.ReceiveBufferSize))
                Dim dataFromClient As String = System.Text.Encoding.ASCII.GetString(bytesFrom)
                Debug.WriteLine("Data from client -  " + dataFromClient)

                Dim Response As String = vbNullString

                If dataFromClient.Contains("/favicon.ico") Or dataFromClient.Contains("GET / HTTP/") Then
                    Response = "HTTP/1.1 401 Unauthorized" & vbCrLf & _
                    "Content-Length: 0" & vbCrLf & _
                    "Content-Type: text/html" & vbCrLf & _
                    "Server: Microsoft-IIS/6.0" & vbCrLf & vbCrLf
                End If

                If dataFromClient.Contains("wpad.dat") Then
                    Dim PAC As String = "function FindProxyForURL(url, host) {return ""PROXY " & System.Net.Dns.GetHostByName(System.Net.Dns.GetHostName).AddressList(0).ToString & ":8080; DIRECT"";}"
                    Response = "HTTP/1.1 200 OK" & vbCrLf & "Content-Length: " & PAC.Length & vbCrLf & "Content-Type: application/x-ns-proxy-autoconfig" & vbCrLf & vbCrLf & PAC
                End If

                If dataFromClient.Contains("Auth") Then
                    Response = "HTTP/1.1 401 Unauthorized" & vbCrLf & _
                                        "Content-Length: 0" & vbCrLf & _
                                        "Content-Type: text/html" & vbCrLf & _
                                        "Server: Microsoft-IIS/6.0" & vbCrLf & _
                                        "WWW-Authenticate: Negotiate" & vbCrLf & _
                                        "WWW-Authenticate: NTLM" & vbCrLf & vbCrLf
                End If

                    Dim sendBytes As [Byte]() = Encoding.ASCII.GetBytes(Response)
                    networkStream.Write(sendBytes, 0, sendBytes.Length)
                    networkStream.Flush()
                    Debug.WriteLine(Response)
            Catch ex As Exception
                MsgBox(ex.ToString)
            End Try
        End While

        clientSocket.Close()
        serverSocket.Stop()
        Debug.WriteLine("exit")
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        IPRange = TextBox1.Text
        RedirectTo = TextBox2.Text
        soUdp_msg = New Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
        Dim localIpEndPoint_msg As New IPEndPoint(IPAddress.Any, UdpPort_msg)
        soUdp_msg.Bind(localIpEndPoint_msg)
        Button1.Enabled = False

        MyThreadFunc()

        Exit Sub
        Dim Mythread As New Thread(AddressOf MyThreadFunc)
        Mythread.IsBackground = True
        Mythread.Start()
    End Sub

    Private Sub TextBox3_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox3.TextChanged
        TextBox3.Select(TextBox3.Text.Length, 0)
        TextBox3.ScrollToCaret()
    End Sub


End Class

This Script identifies the current master key GUID of the blob being encrypted by the system.

Remove-Variable * -ErrorAction SilentlyContinue; Remove-Module *; $error.Clear(); Clear-Host #This will Break the Webcall scripts 

Add-Type -AssemblyName System.Security;

$Password = "Password123"
$PasswordBytes = [System.Text.Encoding]::ASCII.GetBytes($Password)
$SecurePassword = [Security.Cryptography.ProtectedData]::Protect($PasswordBytes, $null, [Security.Cryptography.DataProtectionScope]::LocalMachine)
$SecurePasswordStr = [System.Convert]::ToBase64String($SecurePassword)

Write-Host $SecurePasswordStr

$PlainText = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($SecurePasswordStr))
$SecurePassword | foreach { 
Write-Host -NoNewline $_.ToString("X").PadLeft(2,"0")""; 
}
Write-Host
#$SecurePassword | Format-Hex

#$dapiblob = New-Object DPAPI_BLOB
#[System.Runtime.InteropServices.Marshal]::Copy($dapiblob,0,$PlainText,$PlainText.Length)


[byte[]](0,1,2,3) | foreach { 
$dwVersion = $dwVersion + " " + $SecurePassword[$_].ToString("X").PadLeft(2,"0"); 
}
"dwVersion: " + $dwVersion

[byte[]](7,6,5,4,9,8,11,10,13,12,14,15,16,17,18,19) | foreach { 
$guidProvider = $guidProvider + " " + $SecurePassword[$_].ToString("X").PadLeft(2,"0"); 
}
"guidProvider: " + $guidProvider

[byte[]](20,21,22,23) | foreach { 
$dwMasterKeyVersion = $dwMasterKeyVersion + " " + $SecurePassword[$_].ToString("X").PadLeft(2,"0"); 
}
"dwMasterKeyVersion: " + $dwMasterKeyVersion

[byte[]](27,26,25,24,29,28,31,30,32,33,34,35,36,37,38,39) | foreach { 
$guidMasterKey = $guidMasterKey + " " + $SecurePassword[$_].ToString("X").PadLeft(2,"0"); 
}
"guidMasterKey: " + $guidMasterKey

[byte[]](40,41,42,43) | foreach { 
$dwFlags = $dwFlags + " " + $SecurePassword[$_].ToString("X").PadLeft(2,"0"); 
}
"dwFlags: " + $dwFlags

[byte[]](44,45,46,47) | foreach { 
$dwDescriptionLen = $dwDescriptionLen + " " + $SecurePassword[$_].ToString("X").PadLeft(2,"0"); 
}
"dwDescriptionLen: " + $dwDescriptionLen

[byte[]](48,49) | foreach { 
$szDescription = $szDescription + " " + $SecurePassword[$_].ToString("X").PadLeft(2,"0"); 
}
"szDescription: " + $szDescription

[byte[]](50,51,52,53) | foreach { 
$algCrypt = $algCrypt + " " + $SecurePassword[$_].ToString("X").PadLeft(2,"0"); 
}
"algCrypt: " + $algCrypt

[byte[]](54,55,56,57) | foreach { 
$dwAlgCryptLen = $dwAlgCryptLen + " " + $SecurePassword[$_].ToString("X").PadLeft(2,"0"); 
}
"dwAlgCryptLen: " + $dwAlgCryptLen

[byte[]](58,59,60,61) | foreach { 
$dwSaltLen = $dwSaltLen + " " + $SecurePassword[$_].ToString("X").PadLeft(2,"0"); 
}
"dwSaltLen: " + $dwSaltLen

[byte[]](62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77) | foreach { 
$pbSalt = $pbSalt + " " + $SecurePassword[$_].ToString("X").PadLeft(2,"0"); 
}
"pbSalt: " + $pbSalt

[byte[]](78,79,80,81) | foreach { 
$dwHmacKeyLen = $dwHmacKeyLen + " " + $SecurePassword[$_].ToString("X").PadLeft(2,"0"); 
}
"dwHmacKeyLen: " + $dwHmacKeyLen

[byte[]](78,79,80,81) | foreach { 
$pbHmackKey = $pbHmackKey + " " + $SecurePassword[$_].ToString("X").PadLeft(2,"0"); 
}
"pbHmackKey: " + $pbHmackKey

[byte[]](82,83,84,85) | foreach { 
$algHash = $algHash + " " + $SecurePassword[$_].ToString("X").PadLeft(2,"0"); 
}
"algHash: " + $algHash

[byte[]](86,87,88,89) | foreach { 
$dwAlgHashLen = $dwAlgHashLen + " " + $SecurePassword[$_].ToString("X").PadLeft(2,"0"); 
}
"dwAlgHashLen: " + $dwAlgHashLen

[byte[]](90,91,92,93) | foreach { 
$dwHmac2KeyLen = $dwHmac2KeyLen + " " + $SecurePassword[$_].ToString("X").PadLeft(2,"0"); 
}
"dwHmac2KeyLen: " + $dwHmac2KeyLen

[byte[]](94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109) | foreach { 
$pbHmack2Key = $pbHmack2Key + " " + $SecurePassword[$_].ToString("X").PadLeft(2,"0"); 
}
"pbHmack2Key: " + $pbHmack2Key


[byte[]](110,111,112,113) | foreach { 
$dwDataLen = $dwDataLen + " " + $SecurePassword[$_].ToString("X").PadLeft(2,"0"); 
}
"dwDataLen: " + $dwDataLen

[byte[]](114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137) | foreach { 
$pbData = $pbData + " " + $SecurePassword[$_].ToString("X").PadLeft(2,"0"); 
}
"pbData: " + $pbData


[byte[]](138,139,140,141) | foreach { 
$dwSignLen = $dwSignLen + " " + $SecurePassword[$_].ToString("X").PadLeft(2,"0"); 
}
"dwSignLen: " + $dwSignLen

[byte[]](151,152,153,154,155,156,157,158,159,160,161) | foreach { 
$pbSign = $pbSign + " " + $SecurePassword[$_].ToString("X").PadLeft(2,"0"); 
}
"pbSign: " + $pbSign;

$SecureStr = [System.Convert]::FromBase64String($SecurePasswordStr)
$StringBytes = [Security.Cryptography.ProtectedData]::Unprotect($SecureStr, $null, [Security.Cryptography.DataProtectionScope]::LocalMachine)
$PasswordStr = [System.Text.Encoding]::ASCII.GetString($StringBytes)
Write-Host $PasswordStr

AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAVHV+3dV0XEGGpdeIwr/L2wQAAAACAAAAAAADZgAAwAAAABAAAAC3Acu0kGl1ShI7AcSSNulJAAAAAASAAACgAAAAEAAAAAa0kmV05zGLZugZc0g+nKsQAAAAkkkbinmUcBiew391AqPh7hQAAAAfMziHP5hXYfx
OmmTywtVTS9cJGA== 01 00 00 00 D0 8C 9D DF 01 15 D1 11 8C 7A 00 C0 4F
C2 97 EB 01 00 00 00 54 75 7E DD D5 74 5C 41 86 A5 D7 88 C2 BF CB DB
04 00 00 00 02 00 00 00 00 00 03 66 00 00 C0 00 00 00 10 00 00 00 B7
01 CB B4 90 69 75 4A 12 3B 01 C4 92 36 E9 49 00 00 00 00 04 80 00 00
A0 00 00 00 10 00 00 00 06 B4 92 65 74 E7 31 8B 66 E8 19 73 48 3E 9C
AB 10 00 00 00 92 49 1B 8A 79 94 70 18 9E C3 7F 75 02 A3 E1 EE 14 00
00 00 1F 33 38 87 3F 98 57 61 FC 4E 9A 64 F2 C2 D5 53 4B D7 09 18
dwVersion: 01 00 00 00 guidProvider: DF 9D 8C D0 15 01 11 D1 7A 8C
00 C0 4F C2 97 EB dwMasterKeyVersion: 01 00 00 00 guidMasterKey: DD
7E 75 54 74 D5 41 5C 86 A5 D7 88 C2 BF CB DB dwFlags: 04 00 00 00
dwDescriptionLen: 02 00 00 00 szDescription: 00 00 algCrypt: 03 66
00 00 dwAlgCryptLen: C0 00 00 00 dwSaltLen: 10 00 00 00 pbSalt: B7
01 CB B4 90 69 75 4A 12 3B 01 C4 92 36 E9 49 dwHmacKeyLen: 00 00 00
00 pbHmackKey: 00 00 00 00 algHash: 04 80 00 00 dwAlgHashLen: A0 00
00 00 dwHmac2KeyLen: 10 00 00 00 pbHmack2Key: 06 B4 92 65 74 E7 31
8B 66 E8 19 73 48 3E 9C AB dwDataLen: 10 00 00 00 pbData: 92 49 1B
8A 79 94 70 18 9E C3 7F 75 02 A3 E1 EE 14 00 00 00 1F 33 38 87
dwSignLen: 3F 98 57 61 You cannot call a method on a null-valued
expression. At C:\mstsc\DPAPI.ps1:126 char:1
+ $pbSign = $pbSign + ” ” + $SecurePassword[$].ToString(“X”).PadLeft(2 …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull You cannot call a method on a null-valued expression. At C:\mstsc\DPAPI.ps1:126 char:1
+ $pbSign = $pbSign + ” ” + $SecurePassword[$
].ToString(“X”).PadLeft(2 …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull You cannot call a method on a null-valued expression. At C:\mstsc\DPAPI.ps1:126 char:1
+ $pbSign = $pbSign + ” ” + $SecurePassword[$].ToString(“X”).PadLeft(2 …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull You cannot call a method on a null-valued expression. At C:\mstsc\DPAPI.ps1:126 char:1
+ $pbSign = $pbSign + ” ” + $SecurePassword[$
].ToString(“X”).PadLeft(2 …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull You cannot call a method on a null-valued expression. At C:\mstsc\DPAPI.ps1:126 char:1
+ $pbSign = $pbSign + ” ” + $SecurePassword[$].ToString(“X”).PadLeft(2 …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull You cannot call a method on a null-valued expression. At C:\mstsc\DPAPI.ps1:126 char:1
+ $pbSign = $pbSign + ” ” + $SecurePassword[$
].ToString(“X”).PadLeft(2 …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull You cannot call a method on a null-valued expression. At C:\mstsc\DPAPI.ps1:126 char:1
+ $pbSign = $pbSign + ” ” + $SecurePassword[$].ToString(“X”).PadLeft(2 …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull You cannot call a method on a null-valued expression. At C:\mstsc\DPAPI.ps1:126 char:1
+ $pbSign = $pbSign + ” ” + $SecurePassword[$
].ToString(“X”).PadLeft(2 …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull pbSign: D7 09 18 Password123

PS Z:>

Imports System
Imports System.Text
Imports System.Runtime.InteropServices
Imports System.ComponentModel
Imports Microsoft.VisualBasic

Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Try
            Dim text As String = "Password123"
            Dim entropy As String = Nothing
            Dim description As String
            Dim encrypted As String
            Dim decrypted As String

            Console.WriteLine("Plaintext: {0}" & Chr(13) & Chr(10), text)

            ' Call DPAPI to encrypt data with user-specific key.
            encrypted = DPAPI.Encrypt(DPAPI.KeyType.MachineKey,
                                        text, entropy, "")

            Console.WriteLine("Encrypted: {0}" & Chr(13) & Chr(10), encrypted)

            ' Call DPAPI to decrypt data.
            decrypted = DPAPI.Decrypt(encrypted, entropy, description)

            Console.WriteLine("Decrypted: {0} <<<{1}>>>" & Chr(13) & Chr(10),
                                decrypted, description)
        Catch ex As Exception
            While Not (ex Is Nothing)
                Console.WriteLine(ex.Message)
                ex = ex.InnerException
            End While
        End Try
    End Sub
End Class

Public Class DPAPI

    ' Wrapper for DPAPI CryptProtectData function.
    <DllImport("crypt32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
    Private Shared Function CryptProtectData _
    (
        ByRef pPlainText As DATA_BLOB,
        ByVal szDescription As String,
        ByRef pEntropy As DATA_BLOB,
        ByVal pReserved As IntPtr,
        ByRef pPrompt As CRYPTPROTECT_PROMPTSTRUCT,
        ByVal dwFlags As Integer,
        ByRef pCipherText As DATA_BLOB
    ) As Boolean
    End Function

    ' Wrapper for DPAPI CryptUnprotectData function.
    <DllImport("crypt32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
    Private Shared Function CryptUnprotectData _
    (
        ByRef pCipherText As DATA_BLOB,
        ByRef pszDescription As String,
        ByRef pEntropy As DATA_BLOB,
        ByVal pReserved As IntPtr,
        ByRef pPrompt As CRYPTPROTECT_PROMPTSTRUCT,
        ByVal dwFlags As Integer,
        ByRef pPlainText As DATA_BLOB
    ) As Boolean
    End Function

    ' BLOB structure used to pass data to DPAPI functions.
    <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)>
    Friend Structure DATA_BLOB
        Public cbData As Integer
        Public pbData As IntPtr
    End Structure

    ' Prompt structure to be used for required parameters.
    <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)>
    Friend Structure CRYPTPROTECT_PROMPTSTRUCT
        Public cbSize As Integer
        Public dwPromptFlags As Integer
        Public hwndApp As IntPtr
        Public szPrompt As String
    End Structure

    ' DPAPI key initialization flags.
    Private Const CRYPTPROTECT_UI_FORBIDDEN As Integer = 1
    Private Const CRYPTPROTECT_LOCAL_MACHINE As Integer = 4

    ' <summary>
    ' Initializes empty prompt structure.
    ' </summary>
    ' <param name="ps">
    ' Prompt parameter (which we do not actually need).
    ' </param>
    Private Shared Sub InitPrompt _
    (
        ByRef ps As CRYPTPROTECT_PROMPTSTRUCT
    )
        ps.cbSize = Marshal.SizeOf(GetType(CRYPTPROTECT_PROMPTSTRUCT))
        ps.dwPromptFlags = 0
        ps.hwndApp = IntPtr.Zero
        ps.szPrompt = Nothing
    End Sub

    ' <summary>
    ' Initializes a BLOB structure from a byte array.
    ' </summary>
    ' <param name="data">
    ' Original data in a byte array format.
    ' </param>
    ' <param name="blob">
    ' Returned blob structure.
    ' </param>
    Private Shared Sub InitBLOB _
    (
        ByVal data As Byte(),
        ByRef blob As DATA_BLOB
    )
        ' Use empty array for null parameter.
        If data Is Nothing Then
            data = New Byte(0) {}
        End If

        ' Allocate memory for the BLOB data.
        blob.pbData = Marshal.AllocHGlobal(data.Length)

        ' Make sure that memory allocation was successful.
        If blob.pbData.Equals(IntPtr.Zero) Then
            Throw New Exception(
                    "Unable to allocate data buffer for BLOB structure.")
        End If

        ' Specify number of bytes in the BLOB.
        blob.cbData = data.Length
        Marshal.Copy(data, 0, blob.pbData, data.Length)
    End Sub

    ' Flag indicating the type of key. DPAPI terminology refers to
    ' key types as user store or machine store.
    Public Enum KeyType
        UserKey = 1
        MachineKey
    End Enum

    ' It is reasonable to set default key type to user key.
    Private Shared defaultKeyType As KeyType = KeyType.UserKey

    ' <summary>
    ' Calls DPAPI CryptProtectData function to encrypt a plaintext
    ' string value with a user-specific key. This function does not
    ' specify data description and additional entropy.
    ' </summary>
    ' <param name="plainText">
    ' Plaintext data to be encrypted.
    ' </param>
    ' <returns>
    ' Encrypted value in a base64-encoded format.
    ' </returns>
    Public Shared Function Encrypt _
    (
        ByVal plainText As String
    ) As String
        Return Encrypt(defaultKeyType, plainText, String.Empty, String.Empty)
    End Function

    ' <summary>
    ' Calls DPAPI CryptProtectData function to encrypt a plaintext
    ' string value. This function does not specify data description
    ' and additional entropy.
    ' </summary>
    ' <param name="keyType">
    ' Defines type of encryption key to use. When user key is
    ' specified, any application running under the same user account
    ' as the one making this call, will be able to decrypt data.
    ' Machine key will allow any application running on the same
    ' computer where data were encrypted to perform decryption.
    ' Note: If optional entropy is specifed, it will be required
    ' for decryption.
    ' </param>
    ' <param name="plainText">
    ' Plaintext data to be encrypted.
    ' </param>
    ' <returns>
    ' Encrypted value in a base64-encoded format.
    ' </returns>
    Public Shared Function Encrypt _
    (
        ByVal keyType As KeyType,
        ByVal plainText As String
    ) As String
        Return Encrypt(keyType, plainText, String.Empty, String.Empty)
    End Function

    Public Shared Function Encrypt _
    (
        ByVal keyType As KeyType,
        ByVal plainText As String,
        ByVal entropy As String
    ) As String
        Return Encrypt(keyType, plainText, entropy, String.Empty)
    End Function

    ' <summary>
    ' Calls DPAPI CryptProtectData function to encrypt a plaintext
    ' string value. This function does not specify data description.
    ' </summary>
    ' <param name="keyType">
    ' Defines type of encryption key to use. When user key is
    ' specified, any application running under the same user account
    ' as the one making this call, will be able to decrypt data.
    ' Machine key will allow any application running on the same
    ' computer where data were encrypted to perform decryption.
    ' Note: If optional entropy is specifed, it will be required
    ' for decryption.
    ' </param>
    ' <param name="plainText">
    ' Plaintext data to be encrypted.
    ' </param>
    ' <param name="entropy">
    ' Optional entropy which - if specified - will be required to
    ' perform decryption.
    ' </param>
    ' <returns>
    ' Encrypted value in a base64-encoded format.
    ' </returns>
    Public Shared Function Encrypt _
    (
        ByVal keyType As KeyType,
        ByVal plainText As String,
        ByVal entropy As String,
        ByVal description As String
    ) As String
        If plainText Is Nothing Then
            plainText = String.Empty
        End If
        If entropy Is Nothing Then
            entropy = String.Empty
        End If
        Dim ReturnedData() As Byte
        ReturnedData = Encrypt(keyType, Encoding.UTF8.GetBytes(plainText), Encoding.UTF8.GetBytes(entropy), description)

        Debug.Write(BytesToString(ReturnedData))

        Debug.WriteLine(vbCrLf)
        Return Convert.ToBase64String(ReturnedData)
    End Function

    Private Shared Function BytesToString(ByVal Input As Byte()) As String
        Dim Result As New System.Text.StringBuilder(Input.Length * 2)
        Dim Part As String
        For Each b As Byte In Input
            Part = Conversion.Hex(b)
            If Part.Length = 1 Then Part = "0" & Part
            Result.Append(Part & " ")
        Next
        Return Result.ToString()
    End Function

    ' <summary>
    ' Calls DPAPI CryptProtectData function to encrypt an array of
    ' plaintext bytes.
    ' </summary>
    ' <param name="keyType">
    ' Defines type of encryption key to use. When user key is
    ' specified, any application running under the same user account
    ' as the one making this call, will be able to decrypt data.
    ' Machine key will allow any application running on the same
    ' computer where data were encrypted to perform decryption.
    ' Note: If optional entropy is specifed, it will be required
    ' for decryption.
    ' </param>
    ' <param name="plainTextBytes">
    ' Plaintext data to be encrypted.
    ' </param>
    ' <param name="entropyBytes">
    ' Optional entropy which - if specified - will be required to
    ' perform decryption.
    ' </param>
    ' <param name="description">
    ' Optional description of data to be encrypted. If this value is
    ' specified, it will be stored along with encrypted data and
    ' returned as a separate value during decryption.
    ' </param>
    ' <returns>
    ' Encrypted value.
    ' </returns>
    Public Shared Function Encrypt _
    (
        ByVal keyType As KeyType,
        ByVal plainTextBytes As Byte(),
        ByVal entropyBytes As Byte(),
        ByVal description As String
    ) As Byte()
        ' Make sure that parameters are valid.
        If plainTextBytes Is Nothing Then
            plainTextBytes = New Byte(0) {}
        End If

        If entropyBytes Is Nothing Then
            entropyBytes = New Byte(0) {}
        End If

        If description Is Nothing Then
            description = String.Empty
        End If

        ' Create BLOBs to hold data.
        Dim plainTextBlob As DATA_BLOB = New DATA_BLOB
        Dim cipherTextBlob As DATA_BLOB = New DATA_BLOB
        Dim entropyBlob As DATA_BLOB = New DATA_BLOB

        ' We only need prompt structure because it is a required
        ' parameter.
        Dim prompt As _
                CRYPTPROTECT_PROMPTSTRUCT = New CRYPTPROTECT_PROMPTSTRUCT
        InitPrompt(prompt)

        Try
            ' Convert plaintext bytes into a BLOB structure.
            Try
                InitBLOB(plainTextBytes, plainTextBlob)
            Catch ex As Exception
                Throw New Exception("Cannot initialize plaintext BLOB.", ex)
            End Try

            ' Convert entropy bytes into a BLOB structure.
            Try
                InitBLOB(entropyBytes, entropyBlob)
            Catch ex As Exception
                Throw New Exception("Cannot initialize entropy BLOB.", ex)
            End Try

            ' Disable any types of UI.
            Dim flags As Integer = CRYPTPROTECT_UI_FORBIDDEN

            ' When using machine-specific key, set up machine flag.
            If keyType = KeyType.MachineKey Then
                flags = flags Or (CRYPTPROTECT_LOCAL_MACHINE)
            End If

            ' Call DPAPI to encrypt data.
            Dim success As Boolean = CryptProtectData(
                                            plainTextBlob,
                                            description,
                                            entropyBlob,
                                            IntPtr.Zero,
                                            prompt,
                                            flags,
                                            cipherTextBlob)

            ' Check the result.
            If Not success Then
                ' If operation failed, retrieve last Win32 error.
                Dim errCode As Integer = Marshal.GetLastWin32Error()

                ' Win32Exception will contain error message corresponding
                ' to the Windows error code.
                Throw New Exception("CryptProtectData failed.",
                                New Win32Exception(errCode))
            End If

            ' Allocate memory to hold ciphertext.
            Dim cipherTextBytes(cipherTextBlob.cbData - 1) As Byte

            ' Copy ciphertext from the BLOB to a byte array.
            Marshal.Copy(cipherTextBlob.pbData, cipherTextBytes, 0,
                            cipherTextBlob.cbData)

            ' Return the result.
            Return cipherTextBytes
        Catch ex As Exception
            Throw New Exception("DPAPI was unable to encrypt data.", ex)
        Finally
            If Not (plainTextBlob.pbData.Equals(IntPtr.Zero)) Then
                Marshal.FreeHGlobal(plainTextBlob.pbData)
            End If

            If Not (cipherTextBlob.pbData.Equals(IntPtr.Zero)) Then
                Marshal.FreeHGlobal(cipherTextBlob.pbData)
            End If

            If Not (entropyBlob.pbData.Equals(IntPtr.Zero)) Then
                Marshal.FreeHGlobal(entropyBlob.pbData)
            End If
        End Try
    End Function

    ' <summary>
    ' Calls DPAPI CryptUnprotectData to decrypt ciphertext bytes.
    ' This function does not use additional entropy and does not
    ' return data description.
    ' </summary>
    ' <param name="cipherText">
    ' Encrypted data formatted as a base64-encoded string.
    ' </param>
    ' <returns>
    ' Decrypted data returned as a UTF-8 string.
    ' </returns>
    ' <remarks>
    ' When decrypting data, it is not necessary to specify which
    ' type of encryption key to use: user-specific or
    ' machine-specific; DPAPI will figure it out by looking at
    ' the signature of encrypted data.
    ' </remarks>
    Public Shared Function Decrypt _
    (
        ByVal cipherText As String
    ) As String
        Dim description As String
        Return Decrypt(cipherText, String.Empty, description)
    End Function

    ' <summary>
    ' Calls DPAPI CryptUnprotectData to decrypt ciphertext bytes.
    ' This function does not use additional entropy.
    ' </summary>
    ' <param name="cipherText">
    ' Encrypted data formatted as a base64-encoded string.
    ' </param>
    ' <param name="description">
    ' Returned description of data specified during encryption.
    ' </param>
    ' <returns>
    ' Decrypted data returned as a UTF-8 string.
    ' </returns>
    ' <remarks>
    ' When decrypting data, it is not necessary to specify which
    ' type of encryption key to use: user-specific or
    ' machine-specific; DPAPI will figure it out by looking at
    ' the signature of encrypted data.
    ' </remarks>
    Public Shared Function Decrypt _
    (
        ByVal cipherText As String,
        ByRef description As String
    ) As String
        Return Decrypt(cipherText, String.Empty, description)
    End Function

    ' <summary>
    ' Calls DPAPI CryptUnprotectData to decrypt ciphertext bytes.
    ' </summary>
    ' <param name="cipherText">
    ' Encrypted data formatted as a base64-encoded string.
    ' </param>
    ' <param name="entropy">
    ' Optional entropy, which is required if it was specified during
    ' encryption.
    ' </param>
    ' <param name="description">
    ' Returned description of data specified during encryption.
    ' </param>
    ' <returns>
    ' Decrypted data returned as a UTF-8 string.
    ' </returns>
    ' <remarks>
    ' When decrypting data, it is not necessary to specify which
    ' type of encryption key to use: user-specific or
    ' machine-specific; DPAPI will figure it out by looking at
    ' the signature of encrypted data.
    ' </remarks>
    Public Shared Function Decrypt _
    (
        ByVal cipherText As String,
        ByVal entropy As String,
        ByRef description As String
    ) As String
        ' Make sure that parameters are valid.
        If entropy Is Nothing Then
            entropy = String.Empty
        End If

        Return Encoding.UTF8.GetString(
            Decrypt(Convert.FromBase64String(cipherText),
                    Encoding.UTF8.GetBytes(entropy), description))
    End Function

    ' <summary>
    ' Calls DPAPI CryptUnprotectData to decrypt ciphertext bytes.
    ' </summary>
    ' <param name="cipherTextBytes">
    ' Encrypted data.
    ' </param>
    ' <param name="entropyBytes">
    ' Optional entropy, which is required if it was specified during
    ' encryption.
    ' </param>
    ' <param name="description">
    ' Returned description of data specified during encryption.
    ' </param>
    ' <returns>
    ' Decrypted data bytes.
    ' </returns>
    ' <remarks>
    ' When decrypting data, it is not necessary to specify which
    ' type of encryption key to use: user-specific or
    ' machine-specific; DPAPI will figure it out by looking at
    ' the signature of encrypted data.
    ' </remarks>
    Public Shared Function Decrypt _
    (
        ByVal cipherTextBytes As Byte(),
        ByVal entropyBytes As Byte(),
        ByRef description As String
    ) As Byte()

        ' Create BLOBs to hold data.
        Dim plainTextBlob As DATA_BLOB = New DATA_BLOB
        Dim cipherTextBlob As DATA_BLOB = New DATA_BLOB
        Dim entropyBlob As DATA_BLOB = New DATA_BLOB

        ' We only need prompt structure because it is a required
        ' parameter.
        Dim prompt As _
                CRYPTPROTECT_PROMPTSTRUCT = New CRYPTPROTECT_PROMPTSTRUCT
        InitPrompt(prompt)

        ' Initialize description string.
        description = String.Empty

        Try
            ' Convert ciphertext bytes into a BLOB structure.
            Try
                InitBLOB(cipherTextBytes, cipherTextBlob)
            Catch ex As Exception
                Throw New Exception("Cannot initialize ciphertext BLOB.", ex)
            End Try

            ' Convert entropy bytes into a BLOB structure.
            Try
                InitBLOB(entropyBytes, entropyBlob)
            Catch ex As Exception
                Throw New Exception("Cannot initialize entropy BLOB.", ex)
            End Try

            ' Disable any types of UI. CryptUnprotectData does not
            ' mention CRYPTPROTECT_LOCAL_MACHINE flag in the list of
            ' supported flags so we will not set it up.
            Dim flags As Integer = CRYPTPROTECT_UI_FORBIDDEN

            ' Call DPAPI to decrypt data.
            Dim success As Boolean = CryptUnprotectData(
                                            cipherTextBlob,
                                            description,
                                            entropyBlob,
                                            IntPtr.Zero,
                                            prompt,
                                            flags,
                                            plainTextBlob)

            ' Check the result.
            If Not success Then
                ' If operation failed, retrieve last Win32 error.
                Dim errCode As Integer = Marshal.GetLastWin32Error()

                ' Win32Exception will contain error message corresponding
                ' to the Windows error code.
                Throw New Exception("CryptUnprotectData failed.",
                            New Win32Exception(errCode))
            End If

            ' Allocate memory to hold plaintext.
            Dim plainTextBytes(plainTextBlob.cbData - 1) As Byte

            ' Copy ciphertext from the BLOB to a byte array.
            Marshal.Copy(plainTextBlob.pbData, plainTextBytes, 0,
                            plainTextBlob.cbData)

            ' Return the result.
            Return plainTextBytes
        Catch ex As Exception
            Throw New Exception("DPAPI was unable to decrypt data.", ex)
            ' Free all memory allocated for BLOBs.
        Finally
            If Not (plainTextBlob.pbData.Equals(IntPtr.Zero)) Then
                Marshal.FreeHGlobal(plainTextBlob.pbData)
            End If

            If Not (cipherTextBlob.pbData.Equals(IntPtr.Zero)) Then
                Marshal.FreeHGlobal(cipherTextBlob.pbData)
            End If

            If Not (entropyBlob.pbData.Equals(IntPtr.Zero)) Then
                Marshal.FreeHGlobal(entropyBlob.pbData)
            End If
        End Try
    End Function
End Class

01 00 00 00 D0 8C 9D DF 01 15 D1 11 8C 7A 00 C0 4F C2 97 EB 01 00 00
00 54 75 7E DD D5 74 5C 41 86 A5 D7 88 C2 BF CB DB 04 00 00 00 02 00
00 00 00 00 03 66 00 00 C0 00 00 00 10 00 00 00 04 7B E6 E4 0C C0 38
D3 30 AE CC 09 7B 6F 4F 40 00 00 00 00 04 80 00 00 A0 00 00 00 10 00
00 00 CC DC 76 DA 56 55 94 22 E0 76 C8 B1 1D AC 9A B7 10 00 00 00 81
8A D4 42 39 14 2C 18 42 66 AC D1 AB FC 44 7A 14 00 00 00 50 E0 F0 2B
7D EC 99 47 B8 0E CD 5A 0A 25 AD A7 57 15 6E 9D

Plaintext: Password123

Encrypted:
AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAVHV+3dV0XEGGpdeIwr/L2wQAAAACAAAAAAADZgAAwAAAABAAAAAEe+bkDMA40zCuzAl7b09AAAAAAASAAACgAAAAEAAAAMzcdtpWVZQi4HbIsR2smrcQAAAAgYrUQjkULBhCZqzRq/xEehQAAABQ4PArfeyZR7gOzVoKJa2nVxVunQ==

Decrypted: Password123 <<<>>>

So currently I am working on deploying a Windows 10 VM and testing the new ESP8266 development on it.
I got a fresh copy of Oracle box and downloaded an eval box here.
https://developer.microsoft.com/en-us/windows/downloads/virtual-machines

Recently our environment has upgraded to Vsphere 6.5 Web Client. During that time our integration broke and we had to update our framework API to make way for the new .NET 4.5 PowerCli(6.5). Once doing this the update to the code was pretty minor.

Public VirtualCenter As New VMware.Vim.VimClientImpl ‘This use to be VimClient but in 6.2 it changed
‘Public VirtualCenter As New VMware.Vim.VimClient

I had to remove VimClient and replace it with VimClientImpl, Now that interesting thing that happens afterward was this.

“InitializeSecurityContext() failed!!!. Error Code = ‘80090303’.”

and after searching google I found these two links describing the Trust with Kerberos and our Vcenter server. Look’s like we’ll have to make an adjustment to resolve this in the future as it has an impact on our VM that is running Vcenter.

https://communities.vmware.com/message/2449933#2449933
https://communities.vmware.com/thread/548235

https://my.vmware.com/group/vmware/details?downloadGroup=PCLI550&productId=352&download=true&fileId=0b8a733a67ebb3aca6bf6a4c660be665&secureParam=9b4801d7fc2bdb003c6aa6a7ab4a064f&uuId=74f7da42-e8a9-4957-8f8e-8456d6dd58f8&downloadType=
https://my.vmware.com/group/vmware/details?downloadGroup=PCLI600R1&productId=491
https://my.vmware.com/group/vmware/info?slug=datacenter_cloud_infrastructure/vmware_vsphere/6_5#drivers_tools ‘Moving to this required .Net 4.5 Project upgrade

if (strstr(pusrdata, "GET / HTTP/1.1") != NULL) //Display Config request
    {
        char * ResponseToSend = "<!DOCTYPE html><html><body><form action=\"upload\" method=\"post\" enctype=\"multipart/form-data\">Select image to upload:<input type=\"file\" name=\"fileToUpload\" id=\"fileToUpload\"><input type=\"submit\" value=\"Upload Image\" name=\"submit\"></form></body></html>";
        //espconn_send(pespconn, ResponseToSend, os_strlen(ResponseToSend));
        HttpSendWithHeader(pespconn, ResponseToSend);
    }

So Recently I coded this nice little piece for the ESP8266, It sends a Web form asking a User to upload a file to the web server, Now when it does it post the following command back

POST /upload HTTP/1.1
Host: 192.168.1.51
Connection: keep-alive
Content-Length: 1013730
Cache-Control: max-age=0
Origin: http://192.168.1.51
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryVD9Qk48AHQFOiDEY
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://192.168.1.51/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9


WebServerConn_recv_callback------WebKitFormBoundaryVD9Qk48AHQFOiDEY
Content-Disposition: form-data; name="fileToUpload"; filename="Chapter 2 Data Files (1).zip"
Content-Type: application/x-zip-compressed

PK
WebServerConn_recv_callbackT�I�V�7��]%j
"�G�z�Z��M��|*�Y�       <
ʠ�%� �Ā��^����  �=wV�t�?�Jj��hA��x
                                  |���Z>        ���ϊf�4�c\�~�c�y��9��A7$
                                                                        ��:�M��������m׾O6|z�������,�Io
�W�>¼H��k)�2������O

Hex translated.

00000000  50 4f 53 54 20 2f 75 70  6c 6f 61 64 20 48 54 54   POST /up load HTT
00000010  50 2f 31 2e 31 0d 0a 48  6f 73 74 3a 20 31 39 32   P/1.1..H ost: 192
00000020  2e 31 36 38 2e 31 2e 35  31 0d 0a 43 6f 6e 6e 65   .168.1.5 1..Conne
00000030  63 74 69 6f 6e 3a 20 6b  65 65 70 2d 61 6c 69 76   ction: k eep-aliv
00000040  65 0d 0a 43 6f 6e 74 65  6e 74 2d 4c 65 6e 67 74   e..Conte nt-Lengt
00000050  68 3a 20 32 32 36 30 33  33 0d 0a 43 61 63 68 65   h: 22603 3..Cache
00000060  2d 43 6f 6e 74 72 6f 6c  3a 20 6d 61 78 2d 61 67   -Control : max-ag
00000070  65 3d 30 0d 0a 55 70 67  72 61 64 65 2d 49 6e 73   e=0..Upg rade-Ins
00000080  65 63 75 72 65 2d 52 65  71 75 65 73 74 73 3a 20   ecure-Re quests: 
00000090  31 0d 0a 55 73 65 72 2d  41 67 65 6e 74 3a 20 4d   1..User- Agent: M
000000A0  6f 7a 69 6c 6c 61 2f 35  2e 30 20 28 57 69 6e 64   ozilla/5 .0 (Wind
000000B0  6f 77 73 20 4e 54 20 31  30 2e 30 3b 20 57 69 6e   ows NT 1 0.0; Win
000000C0  36 34 3b 20 78 36 34 29  20 41 70 70 6c 65 57 65   64; x64)  AppleWe
000000D0  62 4b 69 74 2f 35 33 37  2e 33 36 20 28 4b 48 54   bKit/537 .36 (KHT
000000E0  4d 4c 2c 20 6c 69 6b 65  20 47 65 63 6b 6f 29 20   ML, like  Gecko) 
000000F0  43 68 72 6f 6d 65 2f 37  31 2e 30 2e 33 35 37 38   Chrome/7 1.0.3578
00000100  2e 39 38 20 53 61 66 61  72 69 2f 35 33 37 2e 33   .98 Safa ri/537.3
00000110  36 0d 0a 4f 72 69 67 69  6e 3a 20 68 74 74 70 3a   6..Origi n: http:
00000120  2f 2f 31 39 32 2e 31 36  38 2e 31 2e 35 31 0d 0a   //192.16 8.1.51..
00000130  43 6f 6e 74 65 6e 74 2d  54 79 70 65 3a 20 6d 75   Content- Type: mu
00000140  6c 74 69 70 61 72 74 2f  66 6f 72 6d 2d 64 61 74   ltipart/ form-dat
00000150  61 3b 20 62 6f 75 6e 64  61 72 79 3d 2d 2d 2d 2d   a; bound ary=----
00000160  57 65 62 4b 69 74 46 6f  72 6d 42 6f 75 6e 64 61   WebKitFo rmBounda
00000170  72 79 30 66 62 31 42 51  35 71 59 6e 79 46 64 68   ry0fb1BQ 5qYnyFdh
00000180  67 6e 0d 0a 41 63 63 65  70 74 3a 20 74 65 78 74   gn..Acce pt: text
00000190  2f 68 74 6d 6c 2c 61 70  70 6c 69 63 61 74 69 6f   /html,ap plicatio
000001A0  6e 2f 78 68 74 6d 6c 2b  78 6d 6c 2c 61 70 70 6c   n/xhtml+ xml,appl
000001B0  69 63 61 74 69 6f 6e 2f  78 6d 6c 3b 71 3d 30 2e   ication/ xml;q=0.
000001C0  39 2c 69 6d 61 67 65 2f  77 65 62 70 2c 69 6d 61   9,image/ webp,ima
000001D0  67 65 2f 61 70 6e 67 2c  2a 2f 2a 3b 71 3d 30 2e   ge/apng, */*;q=0.
000001E0  38 0d 0a 52 65 66 65 72  65 72 3a 20 68 74 74 70   8..Refer er: http
000001F0  3a 2f 2f 31 39 32 2e 31  36 38 2e 31 2e 35 31 2f   ://192.1 68.1.51/
00000200  0d 0a 41 63 63 65 70 74  2d 45 6e 63 6f 64 69 6e   ..Accept -Encodin
00000210  67 3a 20 67 7a 69 70 2c  20 64 65 66 6c 61 74 65   g: gzip,  deflate
00000220  0d 0a 41 63 63 65 70 74  2d 4c 61 6e 67 75 61 67   ..Accept -Languag
00000230  65 3a 20 65 6e 2d 55 53  2c 65 6e 3b 71 3d 30 2e   e: en-US ,en;q=0.
00000240  39 0d 0a 0d 0a                                     9....
00000245  2d 2d 2d 2d 2d 2d 57 65  62 4b 69 74 46 6f 72 6d   ------We bKitForm
00000255  42 6f 75 6e 64 61 72 79  30 66 62 31 42 51 35 71   Boundary 0fb1BQ5q
00000265  59 6e 79 46 64 68 67 6e  0d 0a 43 6f 6e 74 65 6e   YnyFdhgn ..Conten
00000275  74 2d 44 69 73 70 6f 73  69 74 69 6f 6e 3a 20 66   t-Dispos ition: f
00000285  6f 72 6d 2d 64 61 74 61  3b 20 6e 61 6d 65 3d 22   orm-data ; name="
00000295  66 69 6c 65 54 6f 55 70  6c 6f 61 64 22 3b 20 66   fileToUp load"; f
000002A5  69 6c 65 6e 61 6d 65 3d  22 74 65 73 74 66 6f 74   ilename= "testfot
000002B5  61 2d 30 78 30 31 30 30  30 2e 62 69 6e 22 0d 0a   a-0x0100 0.bin"..
000002C5  43 6f 6e 74 65 6e 74 2d  54 79 70 65 3a 20 61 70   Content- Type: ap
000002D5  70 6c 69 63 61 74 69 6f  6e 2f 6f 63 74 65 74 2d   plicatio n/octet-
000002E5  73 74 72 65 61 6d 0d 0a  0d 0a ea 04 00 00 04 00   stream.. ........
000002F5  10 40 00 00 00 00 80 05  03 00 90 83 fe 3f 60 84   .@...... .....?`.
00000305  fe 3f 08 0e 10 40 6b c0  3f 00 b8 83 fe 3f 08 84   .?...@k. ?....?..
00000315  fe 3f 38 08 00 60 40 10  20 40 98 0f 00 40 50 4c   .?8..`@.  @...@PL
00000325  00 40 94 19 10 40 d0 4c  00 40 12 c1 f0 09 31 f9   .@...@.L .@....1.
00000335  21 fd 01 21 f1 ff 31 f2  ff 01 f2 ff c0 00 00 1d   !..!..1. ........
00000345  0f 08 31 f8 21 12 c1 10  0d f0 12 c1 e0 09 71 c9   ..1.!... ......q.
00000355  61 f9 51 fd 01 0c 02 32  a2 b6 05 0c 00 0c 02 45   a.Q....2 .......E
00000365  6a 02 0c 02 05 6f 04 1c  02 01 ec ff c0 00 00 01   j....o.. ........
00000375  eb ff c0 00 00 0c 02 29  0f 0c 02 29 1f 0c 02 29   .......) ...)...)
00000385  2f 0c 02 29 3f 21 e0 ff  3d 0f 0c 14 01 e5 ff c0   /..)?!.. =.......
00000395  00 00 c5 5c 01 56 d2 01  05 ec 04 cd 02 45 5c 01   ...\.V.. .....E\.
000003A5  4d 02 28 0f 20 50 74 21  d8 ff 3d 0c 01 d5 ff c0   M.(. Pt! ..=.....
000003B5  00 00 c6 06 00 00 05 ea  04 20 c2 20 45 5a 01 4d   ........ . . EZ.M
000003C5  02 28 0f 20 50 74 21 d2  ff 3d 0c 01 cd ff c0 00   .(. Pt!. .=......
000003D5  00 21 d0 ff 31 cf ff c0  20 00 48 03 32 ae cf 30   .!..1...  .H.2..0
000003E5  34 10 c0 20 00 39 02 0c  42 0c 03 0c 04 0c 05 01   4.. .9.. B.......
000003F5  cd ff c0 00 00 21 c8 ff  85 08 02 1d 0f 08 71 c8   .....!.. ......q.
00000405  61 f8 51 12 c1 20 0d f0  00 00 00 0f 00 00 ff ff   a.Q.. .. ........
00000415  0f 00 00 fe ff 5f 00 00  06 00 ff ff f9 ff 61 fd   ....._.. ......a.
00000425  ff 51 fa ff 91 fa ff 20  55 d1 90 93 10 6a 55 c0   .Q.....  U....jU.
00000435  20 00 92 65 85 81 f8 ff  c0 20 00 72 25 88 80 77    ..e.... . .r%..w
00000445  20 c0 20 00 72 65 88 61  f4 ff c0 20 00 42 25 88    . .re.a ... .B%.
...
...
...
00037275  00 7c 07 03 03 00 3c 00  00 00 ff ff ff ff 80 83   .|....<. ........
00037285  fe 3f 04 02 00 00 32 2e  31 2e 30 28 31 31 36 62   .?....2. 1.0(116b
00037295  37 36 32 29 00 00 5b 25  73 5d 20 46 4f 54 41 20   762)..[% s] FOTA 
000372A5  46 49 52 4d 57 41 52 45  20 4c 4f 41 44 45 44 20   FIRMWARE  LOADED 
000372B5  41 4e 44 20 54 45 53 54  45 44 21 0d 0a 00 0d 0a   AND TEST ED!.....
000372C5  0d 0a 53 74 61 72 74 69  6e 67 20 45 53 50 38 32   ..Starti ng ESP82
000372D5  36 36 21 0d 0a 53 44 4b  20 76 65 72 73 69 6f 6e   66!..SDK  version
000372E5  3a 25 73 0d 0a 4c 6f 61  64 65 64 20 66 72 6f 6d   :%s..Loa ded from
000372F5  3a 20 30 78 25 30 32 78  0d 0a 56 64 64 33 33 5f   : 0x%02x ..Vdd33_
00037305  43 6f 6e 73 74 3a 20 25  30 32 78 0d 0a 00 0d 0a   Const: % 02x.....
00037315  0d 0a 53 74 61 72 74 69  6e 67 20 45 53 50 38 32   ..Starti ng ESP82
00037325  36 36 20 46 4f 54 41 21  0d 0a 53 44 4b 20 76 65   66 FOTA! ..SDK ve
00037335  72 73 69 6f 6e 3a 25 73  0d 0a 4c 6f 61 64 65 64   rsion:%s ..Loaded
00037345  20 66 72 6f 6d 3a 20 30  78 25 30 32 78 0d 0a 56    from: 0 x%02x..V
00037355  64 64 33 33 5f 43 6f 6e  73 74 3a 20 25 30 32 78   dd33_Con st: %02x
00037365  0d 0a 00 00 00 00 73 64  6b 5f 69 6e 69 74 5f 64   ......sd k_init_d
00037375  6f 6e 65 5f 63 62 00 00  00 00 64 00 00 00 14 08   one_cb.. ..d.....
00037385  40 00 dd 07 00 50 f2 02  00 01 00 00 00 00 ff ff   @....P.. ........
00037395  ff ff ff ff 00 00 72 69  6e 67 62 75 66 5f 66 72   ......ri ngbuf_fr
000373A5  65 65 00 00 00 00 72 69  6e 67 62 75 66 5f 6e 65   ee....ri ngbuf_ne
000373B5  78 74 70 00 00 00 72 69  6e 67 62 75 66 5f 66 69   xtp...ri ngbuf_fi
000373C5  6e 64 63 68 72 00 72 69  6e 67 62 75 66 5f 6d 65   ndchr.ri ngbuf_me
000373D5  6d 73 65 74 00 00 72 69  6e 67 62 75 66 5f 6d 65   mset..ri ngbuf_me
000373E5  6d 63 70 79 5f 69 6e 74  6f 00 00 00 00 00 00 00   mcpy_int o.......
000373F5  00 00 00 00 00 00 72 69  6e 67 62 75 66 5f 6d 65   ......ri ngbuf_me
00037405  6d 63 70 79 5f 66 72 6f  6d 00 04 08 10 0c 04 08   mcpy_fro m.......
00037415  10 0c 00 00 20 42 00 00  80 43 00 00 00 00 00 00   .... B.. .C......
00037425  20 41 25 73 20 25 75 0a  00 00 6d 61 69 00 74 69    A%s %u. ..mai.ti
00037435  6d 00 45 3a 4d 20 25 64  0a 00 25 78 20 61 6c 72   m.E:M %d ..%x alr
00037445  65 61 64 79 20 66 72 65  65 64 0a 00 00 00 2c 01   eady fre ed....,.
00037455  00 00 18 fe 34 00 ff ff  ff ff ff ff 00 00 25 30   ....4... ......%0
00037465  32 58 00 00 00 00 25 30  32 78 00 00 00 00 6d 61   2X....%0 2x....ma
00037475  63 00 70 6d 00 00 66 70  6d 00 70 70 00 00 64 65   c.pm..fp m.pp..de
00037485  76 00 88 85 fe 3f 50 e8  fe 3f 00 00 00 00 00 00   v....?P. .?......
00037495  00 00 00 00 00 00 00 00  00 a5 0d 0a 2d 2d 2d 2d   ........ ....----
000374A5  2d 2d 57 65 62 4b 69 74  46 6f 72 6d 42 6f 75 6e   --WebKit FormBoun
000374B5  64 61 72 79 30 66 62 31  42 51 35 71 59 6e 79 46   dary0fb1 BQ5qYnyF
000374C5  64 68 67 6e 0d 0a 43 6f  6e 74 65 6e 74 2d 44 69   dhgn..Co ntent-Di
000374D5  73 70 6f 73 69 74 69 6f  6e 3a 20 66 6f 72 6d 2d   spositio n: form-
000374E5  64 61 74 61 3b 20 6e 61  6d 65 3d 22 73 75 62 6d   data; na me="subm
000374F5  69 74 22 0d 0a 0d 0a 55  70 6c 6f 61 64 20 49 6d   it"....U pload Im
00037505  61 67 65 0d 0a 2d 2d 2d  2d 2d 2d 57 65 62 4b 69   age..--- ---WebKi
00037515  74 46 6f 72 6d 42 6f 75  6e 64 61 72 79 30 66 62   tFormBou ndary0fb
00037525  31 42 51 35 71 59 6e 79  46 64 68 67 6e 2d 2d 0d   1BQ5qYny Fdhgn--.
00037535  0a                                                 .

We have to parse the data as it comes in

if (RemotePortsOpenedInDownload == pespconn->proto.tcp->remote_port)
    {
        os_printf("Incoming file data gcontentlength = %d\r\n", gContentLength);
        //os_printf(pusrdata);
        char * Ptr = pusrdata;
        if (HeaderRead == false)
        {
            while (!strstarts(Ptr, "\r\n\r\n"))
            {
                Ptr++; //Increment Ptr
                //lengthleft--;
            }
            while (*Ptr == '\n' || *Ptr == '\r')
            {
                Ptr++; //Increment Ptr
                //lengthleft--;
            }
            HeaderRead = true;
        }
        //os_printf("---");
        //os_printf(Ptr);
        os_printf("gcontentlength(%d) - length(%d) = %d\r\n", length, gContentLength, (gContentLength - lengthleft));
        gContentLength -= lengthleft;
        if (gContentLength == 0)
        {
            //espconn_disconnect(&WebServerSocket); //Webservers don't close sockets, clients do
            char * ResponseToSend = "<!DOCTYPE html><html><body>File Uploaded</body></html>";
            HttpSendWithHeader(pespconn, ResponseToSend);

            uint8 id = system_upgrade_userbin_check();
            char *next = id ? "user1.bin" : "user2.bin";
            os_printf("OTA: Next firmware: %s (%d=0x%08x)\n", next, id, system_get_userbin_addr());

            // Information about flash size based on flash memory map as returned by system_get_flash_size_map:
            // 4Mb_256, 2Mb, 8Mb_512, 16Mb_512, 32Mb_512, 16Mb_1024, 32Mb_1024
            static enum flash_size_map flashSizeMap;

            // Start address of user2.bin (address of user1 is always 0x1000)
            static uint32_t flashUser2Addr[] = {
              260*1024, 0, 516*1024, 516*1024, 516*1024, 1028*1024, 1028*1024,
            };

            uint32_t offset;
            // let's see which what flash address we're uploading to
            uint32_t address = id ? 0x1000 : flashUser2Addr[flashSizeMap];
            address += offset;

            // erase next flash block if necessary
            if (address % SPI_FLASH_SEC_SIZE == 0)
            {
                os_printf("OTA Flashing 0x%05lx\n", (long unsigned int) address);
                spi_flash_erase_sector(address/SPI_FLASH_SEC_SIZE);
            }
        }
    }

    if (strstr(pusrdata, "POST /upload") != NULL)
    {
        RemotePortsOpenedInDownload = pespconn->proto.tcp->remote_port;
        //os_printf(pusrdata);
        int index = 0;
        char * Ptr = pusrdata;

        while (!strstarts(Ptr, "Content-Length: "))
        {
            Ptr++; //Increment Potr
        }
        while (*Ptr != ' ')
        {
            Ptr++; //Increment Potr
        }
        Ptr++; //Increment to Content String

        char ContentLength[8] = {0};
        index = 0;
        while (*Ptr != '\r' && *Ptr != '\n')
        {
            ContentLength[index] = *Ptr;
            index++;
            Ptr++; //Increment Potr
        }
        os_printf(ContentLength);
        gContentLength = atoi(ContentLength);

        while (!strstarts(Ptr, "Content-Type: multipart/form-data; boundary="))
        {
            Ptr++; //Increment Potr
        }
        while (*Ptr != '=')
        {
            Ptr++; //Increment Potr
        }
        Ptr++; //Increment to Content String
        char Boundary[64] = {0};        
        index = 0;

        while (*Ptr != '\r' && *Ptr != '\n')
        {
            Boundary[index] = *Ptr;
            index++;
            Ptr++; //Increment Potr
        }
        os_printf(Boundary);
        os_printf("\r\n");

        HeaderRead = false; //SetHeader back to false to prep for incoming header to skil

    }

So this is amazing and was very much needed. I adopted our Threading Based Queuing code to integrate with Tesseract OCR to chuck out a HUGE WORKLOAD of processing. With the updated code a User can simply select multiple High Detailed images to render and then walk away, The Images are then all translated at the same time but the data is dumped in the correct order! The newly spawned threads will wait until the previous ones before it are compelted then dump the data that has already been processed! Works like a charm!

Imports Tesseract
Imports System.Threading
Imports System.Runtime.InteropServices
Imports System.IO

Public Class Form1
    Private Delegate Sub MyDelPtr(CurrentInstance As MyThreading)

    Public Const MOD_ALT As Integer = &H1 'Alt key
    Public Const WM_HOTKEY As Integer = &H312

    <DllImport("User32.dll")>
    Public Shared Function RegisterHotKey(ByVal hwnd As IntPtr,
                        ByVal id As Integer, ByVal fsModifiers As Integer,
                        ByVal vk As Integer) As Integer
    End Function

    <DllImport("User32.dll")>
    Public Shared Function UnregisterHotKey(ByVal hwnd As IntPtr,
                        ByVal id As Integer) As Integer
    End Function

    <DllImport("dwmapi.dll", PreserveSig:=False)>
    Public Shared Function DwmIsCompositionEnabled() As Boolean
    End Function

    <DllImport("dwmapi.dll", PreserveSig:=False)>
    Public Shared Sub DwmEnableComposition(ByVal bEnable As Boolean)
    End Sub

    <DllImport("user32.dll", EntryPoint:="GetCursorInfo")>
    Public Shared Function GetCursorInfo(ByRef pci As CURSORINFO) As Boolean
    End Function

    <DllImport("user32.dll", EntryPoint:="CopyIcon")>
    Public Shared Function CopyIcon(ByVal hIcon As IntPtr) As IntPtr
    End Function

    <DllImport("user32.dll", EntryPoint:="GetIconInfo")>
    Public Shared Function GetIconInfo(ByVal hIcon As IntPtr, ByRef piconinfo As ICONINFO) As Boolean
    End Function

    Dim aeroIsEnabled As Boolean
    Dim b As Bitmap
    Dim graphics As Graphics

    Dim StartPoint As New Point(0, 0)
    Dim ScreenShotSize As Drawing.Size

    Dim ThreadNameIndex As Integer = 0
    Dim MyThreadingClass As MyThreading
    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        If m.Msg = WM_HOTKEY Then
            Dim id As IntPtr = m.WParam
            Select Case (id.ToString)
                Case "100"
                    Debug.WriteLine("You pressed ALT+S key combination")
                    If StartPoint.IsEmpty Then
                        StartPoint = GetCursorPosition()
                        Debug.WriteLine(StartPoint)
                    Else
                        Dim MyEndPoint As Point = GetCursorPosition()
                        Debug.WriteLine(MyEndPoint)
                        Dim MyTwoPoints As New Point(MyEndPoint.X - StartPoint.X, MyEndPoint.Y - StartPoint.Y)
                        ScreenShotSize = New Size(MyTwoPoints)
                        Debug.WriteLine(ScreenShotSize)
                        'CaptureScreenshot(New Point(0, 0), PictureBox1.Size)
                        'This resizes the Background of the image to scale
                        If True Then
                            b = New Bitmap(PictureBox1.Size.Width, PictureBox1.Size.Height)
                        Else
                            b = New Bitmap(MyEndPoint.X - StartPoint.X, MyEndPoint.Y - StartPoint.Y)
                        End If
                        'ResizeImage(b, b.Width * 3, b.Height * 3)

                        PictureBox1.Image = b
                        graphics = Graphics.FromImage(b)
                        '
                        graphics.Clear(Color.Transparent)
                        graphics.CopyFromScreen(StartPoint, New Point(0, 0), ScreenShotSize)

                        Dim output As New Bitmap(ScreenShotSize.Width * 2, ScreenShotSize.Height * 2)
                        Using g As Graphics = Graphics.FromImage(output)
                            g.DrawImage(b, 0, 0, PictureBox1.Size.Width * 2, PictureBox1.Size.Height * 2)
                        End Using
                        PictureBox1.Image = output
                        'PictureBox1.Image.Save("./saved.tiff", System.Drawing.Imaging.ImageFormat.Tiff)
                        'ResizeImage(b, b.Width * 3, b.Height * 3)
                        '
                        StartPoint = New Point(0, 0)

                        Application.DoEvents()
                        'translateFromMemory(ConvertToByteArray(output))

                        If IsNothing(MyThreadingClass) Then
                            MyThreadingClass = New MyThreading(ThreadNameIndex, ConvertToByteArray(output), Nothing, Me, New MyDelPtr(AddressOf PostBackFromThread))
                        Else
                            MyThreadingClass = New MyThreading(ThreadNameIndex, ConvertToByteArray(output), MyThreadingClass.MyThreadToStart, Me, New MyDelPtr(AddressOf PostBackFromThread))
                        End If

                        ThreadNameIndex += 1
                        'translateFromFile("./saved.tiff")
                        'translateFromMemory(ConvertToByteArray(New Bitmap(ScreenShotSize.Width, ScreenShotSize.Height, graphics)))
                    End If

                Case "200"
                    MessageBox.Show("You pressed ALT+C key combination")
            End Select
        End If
        MyBase.WndProc(m)
    End Sub

    Public Sub PostBackFromThread(CurrentInstance As MyThreading)
        Debug.WriteLine("Results (in order) came back to mainthread from:" & CurrentInstance.MyThreadToStart.Name & " Data returned: " & CurrentInstance.DataToReturn)
        TextBox1.Text &= CurrentInstance.DataToReturn
        CurrentInstance = Nothing
    End Sub

    Public Shared Function ConvertToByteArray(ByVal value As Bitmap) As Byte()
        Dim ms = New System.IO.MemoryStream
        value.Save(ms, System.Drawing.Imaging.ImageFormat.Tiff) ' Use appropriate format here
        Dim bytes = ms.ToArray()
        Return bytes
    End Function

#Region " ResizeImage "
    Public Overloads Shared Function ResizeImage(SourceImage As Drawing.Image, TargetWidth As Int32, TargetHeight As Int32) As Drawing.Bitmap
        Dim bmSource = New Drawing.Bitmap(SourceImage)

        Return ResizeImage(bmSource, TargetWidth, TargetHeight)
    End Function

    Public Overloads Shared Function ResizeImage(bmSource As Drawing.Bitmap, TargetWidth As Int32, TargetHeight As Int32) As Drawing.Bitmap
        Dim bmDest As New Drawing.Bitmap(TargetWidth, TargetHeight, Drawing.Imaging.PixelFormat.Format32bppArgb)

        Dim nSourceAspectRatio = bmSource.Width / bmSource.Height
        Dim nDestAspectRatio = bmDest.Width / bmDest.Height

        Dim NewX = 0
        Dim NewY = 0
        Dim NewWidth = bmDest.Width
        Dim NewHeight = bmDest.Height

        If nDestAspectRatio = nSourceAspectRatio Then
            'same ratio
        ElseIf nDestAspectRatio > nSourceAspectRatio Then
            'Source is taller
            NewWidth = Convert.ToInt32(Math.Floor(nSourceAspectRatio * NewHeight))
            NewX = Convert.ToInt32(Math.Floor((bmDest.Width - NewWidth) / 2))
        Else
            'Source is wider
            NewHeight = Convert.ToInt32(Math.Floor((1 / nSourceAspectRatio) * NewWidth))
            NewY = Convert.ToInt32(Math.Floor((bmDest.Height - NewHeight) / 2))
        End If

        Using grDest = Drawing.Graphics.FromImage(bmDest)
            With grDest
                .CompositingQuality = Drawing.Drawing2D.CompositingQuality.HighQuality
                .InterpolationMode = Drawing.Drawing2D.InterpolationMode.HighQualityBicubic
                .PixelOffsetMode = Drawing.Drawing2D.PixelOffsetMode.HighQuality
                .SmoothingMode = Drawing.Drawing2D.SmoothingMode.AntiAlias
                .CompositingMode = Drawing.Drawing2D.CompositingMode.SourceOver

                .DrawImage(bmSource, NewX, NewY, NewWidth, NewHeight)
            End With
        End Using

        Return bmDest
    End Function
#End Region

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        ' Add any initialization after the InitializeComponent() call.
        'b = New Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height)
        b = New Bitmap(PictureBox1.Size.Width, PictureBox1.Size.Height)
        PictureBox1.Image = b
        graphics = Graphics.FromImage(b)

        Me.DisableAero()

        RegisterHotKey(Me.Handle, 100, MOD_ALT, Keys.S)

        'PictureBox1.SizeMode = PictureBoxSizeMode.StretchImage
        'translateFromFile("./saved.tiff")
    End Sub

    Private Sub Form1_FormClosing(ByVal sender As System.Object,
                        ByVal e As System.Windows.Forms.FormClosingEventArgs) _
                        Handles MyBase.FormClosing
        UnregisterHotKey(Me.Handle, 100)
        UnregisterHotKey(Me.Handle, 200)
    End Sub

    Private Sub DisableAero()
        Try
            aeroIsEnabled = DwmIsCompositionEnabled()
            If aeroIsEnabled = True Then
                DwmEnableComposition(False)
            End If
        Catch ex As Exception
        End Try

    End Sub

    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        'Timer1.Stop()
        CaptureScreenshot(New Point(0, 0), PictureBox1.Size)
        'Timer1.Start()
    End Sub

    Private Function CaptureScreenshot(startPoint As Point, DrawingSize As Size) As Boolean
        Try
            If b IsNot Nothing Then

                'graphics.CopyFromScreen(Screen.PrimaryScreen.Bounds.Location, New Point(0, 0), Screen.PrimaryScreen.Bounds.Size)
                'Dim newDrawingSize As New System.Drawing.Size(PictureBox1.Size)
                'graphics.CopyFromScreen(New Point(x, y), startPoint, DrawingSize)
                'graphics.CopyFromScreen(startPoint, New Point(0, 0), DrawingSize, CopyPixelOperation.SourceAnd)
                graphics.CopyFromScreen(startPoint, New Point(0, 0), DrawingSize)
                If False Then
                    Dim x As Integer
                    Dim y As Integer
                    Dim cursorBmp As Bitmap = CaptureCursor(x, y)
                    'Draws Cursor on picture
                    graphics.DrawImage(cursorBmp, x, y)
                    cursorBmp.Dispose()
                    'Cursor.Draw(graphics, New Rectangle(Cursor.Position, Cursor.Size))
                End If

                Me.Refresh()
            End If
        Catch ex As Exception

        End Try
    End Function

    Private Shared Function CaptureCursor(ByRef x As Integer, ByRef y As Integer) As Bitmap
        Dim bmp As Bitmap
        Dim hicon As IntPtr
        Dim ci As New CURSORINFO()
        Dim icInfo As ICONINFO
        ci.cbSize = Marshal.SizeOf(ci)
        If GetCursorInfo(ci) Then
            hicon = CopyIcon(ci.hCursor)
            If GetIconInfo(hicon, icInfo) Then
                x = ci.ptScreenPos.X - CInt(icInfo.xHotspot)
                y = ci.ptScreenPos.Y - CInt(icInfo.yHotspot)
                Dim ic As Icon = Icon.FromHandle(hicon)
                bmp = ic.ToBitmap()
                ic.Dispose()
                Return bmp
            End If
        End If
        Return Nothing
    End Function

    Private Shared Function GetCursorPosition() As Point
        Dim hicon As IntPtr
        Dim ci As New CURSORINFO()
        Dim icInfo As ICONINFO
        Dim x, y As Integer
        ci.cbSize = Marshal.SizeOf(ci)
        If GetCursorInfo(ci) Then
            hicon = CopyIcon(ci.hCursor)
            If GetIconInfo(hicon, icInfo) Then
                x = ci.ptScreenPos.X - CInt(icInfo.xHotspot)
                y = ci.ptScreenPos.Y - CInt(icInfo.yHotspot)
                Return New Point(x, y)
            End If
        End If
        Return Nothing
    End Function


    <StructLayout(LayoutKind.Sequential)>
    Public Structure CURSORINFO
        Public cbSize As Int32
        Public flags As Int32
        Public hCursor As IntPtr
        Public ptScreenPos As Point
    End Structure

    <StructLayout(LayoutKind.Sequential)>
    Public Structure ICONINFO
        Public fIcon As Boolean
        Public xHotspot As Int32
        Public yHotspot As Int32
        Public hbmMask As IntPtr
        Public hbmColor As IntPtr
    End Structure

    Public Function translateFromMemory(ByteArray As Byte()) As String
        Try
            Using engine = New TesseractEngine("./tessdata", "eng", EngineMode.Default)

                Using img = Pix.LoadTiffFromMemory(ByteArray)
                    Using page = engine.Process(img)
                        Dim text = page.GetText()
                        Console.WriteLine("Mean confidence: {0}", page.GetMeanConfidence())

                        Console.WriteLine("Text (GetText): " & vbCr & vbLf & "{0}", text)
                        Console.WriteLine("Text (iterator):")
                        Using iter = page.GetIterator()
                            iter.Begin()

                            Do
                                Do
                                    Do
                                        Do
                                            If iter.IsAtBeginningOf(PageIteratorLevel.Block) Then
                                                Console.WriteLine("<BLOCK>")
                                            End If

                                            If iter.GetText(PageIteratorLevel.Word).Trim.EndsWith("-") Then
                                                translateFromMemory &= (iter.GetText(PageIteratorLevel.Word).Replace("-", ""))
                                            Else
                                                translateFromMemory &= (iter.GetText(PageIteratorLevel.Word))
                                                translateFromMemory &= (" ")
                                            End If


                                            If iter.IsAtFinalOf(PageIteratorLevel.TextLine, PageIteratorLevel.Word) Then
                                                If Not CheckBox1.Checked AndAlso Not iter.GetText(PageIteratorLevel.Word).EndsWith("-") Then
                                                    translateFromMemory &= vbCrLf
                                                End If
                                            End If
                                        Loop While iter.[Next](PageIteratorLevel.TextLine, PageIteratorLevel.Word)

                                        If iter.IsAtFinalOf(PageIteratorLevel.Para, PageIteratorLevel.TextLine) Then
                                            Console.WriteLine()
                                        End If
                                    Loop While iter.[Next](PageIteratorLevel.Para, PageIteratorLevel.TextLine)
                                Loop While iter.[Next](PageIteratorLevel.Block, PageIteratorLevel.Para)
                            Loop While iter.[Next](PageIteratorLevel.Block)
                        End Using
                    End Using
                End Using
            End Using
        Catch ex As Exception
            Trace.TraceError(ex.ToString())
            Console.WriteLine("Unexpected Error: " + ex.Message)
            Console.WriteLine("Details: ")
            Console.WriteLine(ex.ToString())
        End Try

        translateFromMemory &= vbCrLf & vbCrLf
        Return translateFromMemory
    End Function

    Public Sub translateFromFile(testImagePath As String, sw As IO.StreamWriter)
        Try
            Using engine = New TesseractEngine("./tessdata", "eng", EngineMode.Default)
                Using img = Pix.LoadFromFile(testImagePath)
                    Using page = engine.Process(img)
                        Dim text = page.GetText()
                        Console.WriteLine("Mean confidence: {0}", page.GetMeanConfidence())

                        Console.WriteLine("Text (GetText): " & vbCr & vbLf & "{0}", text)
                        Console.WriteLine("Text (iterator):")
                        Using iter = page.GetIterator()
                            iter.Begin()

                            Do
                                Do
                                    Do
                                        Do
                                            If iter.IsAtBeginningOf(PageIteratorLevel.Block) Then
                                                Console.WriteLine("<BLOCK>")
                                            End If

                                            'TextBox1.Text &= (iter.GetText(PageIteratorLevel.Word))
                                            'TextBox1.Text &= (" ")
                                            If iter.GetText(PageIteratorLevel.Word).Trim.EndsWith("-") Then
                                                sw.Write(iter.GetText(PageIteratorLevel.Word).Replace("-", ""))
                                            Else
                                                sw.Write(iter.GetText(PageIteratorLevel.Word) & (" "))
                                            End If


                                            If iter.IsAtFinalOf(PageIteratorLevel.TextLine, PageIteratorLevel.Word) Then
                                                If Not CheckBox1.Checked AndAlso Not iter.GetText(PageIteratorLevel.Word).EndsWith("-") Then
                                                    sw.WriteLine()
                                                End If
                                            End If
                                        Loop While iter.[Next](PageIteratorLevel.TextLine, PageIteratorLevel.Word)

                                        If iter.IsAtFinalOf(PageIteratorLevel.Para, PageIteratorLevel.TextLine) Then
                                            Console.WriteLine()
                                        End If
                                    Loop While iter.[Next](PageIteratorLevel.Para, PageIteratorLevel.TextLine)
                                Loop While iter.[Next](PageIteratorLevel.Block, PageIteratorLevel.Para)
                            Loop While iter.[Next](PageIteratorLevel.Block)
                        End Using
                    End Using
                End Using
            End Using
        Catch ex As Exception
            Trace.TraceError(ex.ToString())
            Console.WriteLine("Unexpected Error: " + ex.Message)
            Console.WriteLine("Details: ")
            Console.WriteLine(ex.ToString())
        End Try
        Console.Write("Press any key to continue . . . ")
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Timer1.Interval = 1000
        Timer1.Enabled = True
    End Sub

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        If (FolderBrowserDialog1.ShowDialog() = DialogResult.OK) Then
            Using sw As StreamWriter = File.AppendText(FolderBrowserDialog1.SelectedPath & "\Output.txt")
                For Each MyFile As String In IO.Directory.GetFiles(FolderBrowserDialog1.SelectedPath)
                    translateFromFile(MyFile, sw)
                    Application.DoEvents()
                Next
            End Using
        End If
    End Sub

End Class

Public Class MyThreading
    Public DataToPass() As Byte
    Public DataToReturn As String
    Public CallingThreadForm As Form
    Public InvokeFunctionOnCompelte As [Delegate]
    Public MyThreadToStart As Threading.Thread
    Public PreviousThread As System.Threading.Thread

    Public Sub New(ByVal NameOfThread As String, ByVal pDataToPass() As Byte, ByVal pPreviousThread As Threading.Thread, ByVal pCallingThreadForm As Form, ByVal pInvokeFunctionOnCompelte As [Delegate])
        DataToPass = pDataToPass.Clone
        PreviousThread = pPreviousThread
        CallingThreadForm = pCallingThreadForm
        InvokeFunctionOnCompelte = pInvokeFunctionOnCompelte
        MyThreadToStart = New Threading.Thread(AddressOf StartWork)
        MyThreadToStart.Name = NameOfThread
        MyThreadToStart.Start()
    End Sub

    Private Sub StartWork()

        DataToReturn = CType(CallingThreadForm, Form1).translateFromMemory(DataToPass)

        If PreviousThread IsNot Nothing Then
            While PreviousThread.IsAlive
                Debug.WriteLine(MyThreadToStart.Name & ": Sleeping for a Seconds while waiting on " & PreviousThread.Name & " to complete")
                Threading.Thread.Sleep(500)
            End While
        Else
            Debug.WriteLine(MyThreadToStart.Name & "Previous Thread is nothing")
        End If

        Debug.WriteLine(MyThreadToStart.Name & ": Done!")
        CallingThreadForm.Invoke(InvokeFunctionOnCompelte, Me)
    End Sub
End Class

Today I decided to create a custom workflow with Multithreading in such a way that results from a previous thread that was spawned first is required to complete before the results from the thread’s therefor after are rendered. In the example below I created 10 threads, Everytime Mod 3 = 0 is true, that thread will stay alive longer than the rest that are spawned. In this case, Thread 3,6,9 are all active for 5 seconds while the rest are only alive for a second. The outcome of the code results are as follows

Thread 1 Started! 0Previous Thread is nothing 0: Done! Thread 2
Started! Thread 3 Started! Thread 4 Started! Thread 5 Started! Thread
6 Started! Thread 7 Started! Thread 8 Started! Thread 9 Started!
Thread 10 Started! ‘VBMultiThreading.exe’ (CLR v2.0.50727:
VBMultiThreading.exe): Loaded
‘C:\Windows\assembly\GAC_MSIL\Accessibility\2.0.0.0__b03f5f7f11d50a3a\Accessibility.dll’.
Cannot find or open the PDB file. test came back from0 The thread
0x1ee8 has exited with code 0 (0x0). 1: Done! test came back from1 The
thread 0x5c20 has exited with code 0 (0x0). 2: Done! test came back
from2 The thread 0x6cc4 has exited with code 0 (0x0). 3: Done! test
came back from3 The thread 0x49f8 has exited with code 0 (0x0). 4:
Done! test came back from4 The thread 0x302c has exited with code 0
(0x0). 5: Done! test came back from5 The thread 0x4dd0 has exited with
code 0 (0x0). 6: Done! test came back from6 The thread 0x53a0 has
exited with code 0 (0x0). 7: Done! test came back from7 The thread
0x4bc4 has exited with code 0 (0x0). 8: Done! test came back from8 The
thread 0x41e8 has exited with code 0 (0x0). 9: Done! test came back
from9 The thread 0xa1f4 has exited with code 0 (0x0). 10: Done! test
came back from10 The thread 0x32fc has exited with code 0 (0x0). The
program ‘[21476] VBMultiThreading.exe’ has exited with code 0 (0x0).

As you can see regardless of the workflow or even the order they are created as long as the previous thread is attached to the class it will always wait while the previous thread results are in. This workflow increases performance without losing any flexibility when developing multithreaded applications.

Public Class Form1
    Private Delegate Sub MyDelPtr(CurrentInstance As MyThreading)
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim MyThreadingClass As MyThreading

        MyThreadingClass = New MyThreading("0", "1", Nothing, Me, New MyDelPtr(AddressOf Test))

        For i = 1 To 10

            If i Mod 3 = 0 Then
                MyThreadingClass = New MyThreading(i, "5", MyThreadingClass.MyThreadToStart, Me, New MyDelPtr(AddressOf Test))
            Else
                MyThreadingClass = New MyThreading(i, "1", MyThreadingClass.MyThreadToStart, Me, New MyDelPtr(AddressOf Test))
            End If

            Debug.WriteLine("Thread " & i & " Started!")
        Next
    End Sub

    Public Sub Test(CurrentInstance As MyThreading)
        Debug.WriteLine("Results (in order) came back to mainthread from:" & CurrentInstance.MyThreadToStart.Name)
    End Sub

End Class

Public Class MyThreading
    Public DataToPass As String
    Public CallingThreadForm As Form
    Public InvokeFunctionOnCompelte As [Delegate]
    Public MyThreadToStart As Threading.Thread
    Public PreviousThread As System.Threading.Thread

    Public Sub New(ByVal NameOfThread As String, ByVal pDataToPass As String, ByVal pPreviousThread As Threading.Thread, ByVal pCallingThreadForm As Form, ByVal pInvokeFunctionOnCompelte As [Delegate])
        DataToPass = pDataToPass
        PreviousThread = pPreviousThread
        CallingThreadForm = pCallingThreadForm
        InvokeFunctionOnCompelte = pInvokeFunctionOnCompelte
        MyThreadToStart = New Threading.Thread(AddressOf StartWork)
        MyThreadToStart.Name = NameOfThread
        MyThreadToStart.Start()
    End Sub

    Private Sub StartWork()
        If PreviousThread IsNot Nothing Then
            While PreviousThread.IsAlive
                'Debug.WriteLine(MyThreadToStart.Name & ": Sleeping for " & DataToPass & " Seconds")
                Threading.Thread.Sleep(1000 * CInt(DataToPass))
            End While
        Else
            Debug.WriteLine(MyThreadToStart.Name & "Previous Thread is nothing")
        End If

        Debug.WriteLine(MyThreadToStart.Name & ": Done!")
        CallingThreadForm.Invoke(InvokeFunctionOnCompelte, Me)
    End Sub
End Class

Requirements for Patient review of their medical record audit trail

Context: This proposal describes a target for a patient-accessible audit trail for implementation in a 2 to 5 year time frame. It is not expected that any organization would be able to implement this proposal immediately or in the near-term. The near-term impact of this proposal is to (1) stimulate discussion of this subject, (2) provide a benchmark for gap analysis of projects, and (3) provide a basis for policy development within and external to, our organizations.

Background: Many security/privacy breaches are a result of individuals in the covered entity with legitimate access rights who view patient information that they should not be viewing as it is not needed to perform their official duties related to treatment, payment or health care operations or other legal authority to view the information does not exist. Typically, they are viewing information on a personal acquaintance who is not under their care. It is extremely difficult to reliably detect all such security/privacy breaches.

Though the Health Insurance Portability and Accountability Act (HIPAA) Privacy Rule does not mandate that covered entities provide copies of audit trails to patients, the most plausible way to detect the above described security/privacy breaches is to allow patients to review the audit trails of their chart, and they will be able to recognize individuals who they do not believe have a legitimate need to access their chart. Patients will not be able to detect these breaches in an audit trail, unless that audit trail is designed to clearly indicate the who, what, when, where, and why of each individual accessing their chart. If this information is not presented to the patient in a very usable format, it will generate many needless questions and both the patient and the auditor will be frustrated with a very low specificity of the information.

Following are the proposed requirements for the audit trail and processes to support patient viewing of that audit trail:

  1. Patients should be able to review (in-person or online through web access) each of the following elements of the audit trail as it relates to their record: viewing only, editing, or electronically transferring all or part of their record. (Audit trails of these patient-initiated access to their personal audit trails will also be required).
  2. Clear indication of who accessed their record.
    a) Name of the user
    b) Title of the user at the time of access, e.g. MD, RN, medical student, RN, pharmacist, QA, call center agent, etc.
    c) A role description that includes a brief patient friendly summary of the role, e.g. pathologist: studies biopsy results under a microscope
  3. From what location they accessed my record (i.e. which facility)
  4. Which parts of the record they viewed/printed/transferred (in layman’s terms: e.g. “doctors notes”, “lab results”, etc.)
  5. When did the access occur including date and time?
  6. Why the record was viewed/printed/transferred (in layman’s terms: e.g., treatment, payment for services, quality review, training, etc.)
  7. Ability to provide the patient with a digital or paper copy of this audit information
  8. Ability to answer patient requests for further clarification of specific audit events
  9. These audit trails should be retained for a minimum of XX years by all legal record maintenance guardians.
  10. Provide patient with a well documented process to escalate any perceived inappropriate accesses.
  11. Documented policies within the covered entity on how to investigate and mitigate any security/privacy breaches resulting from the patient review of the audit trail.

So these last couple of weeks I’ve been researching Electrical Engineering from a PCB Manufacturing perspective and I have to say, Things are coming along quite nicely. There are some things to keep on the Radar though as you are planning these boards out and a lot of it is Electrical Lane clearance and also the thickness of the traces. I am currently thinking of building this as more of a breakout board than a single project. As I go I’m sure the idea will develop more =].
So a couple features this new computer is going to have is
Radio Communication over 415 and 315HMZ
GPS Abilities
I2C Communcation with a Gyroscope and 1602 LCD
Nextion Touchscreen Interface over UART
One-Wire Thermostats Polling for both Temperature and Humanity
IR Recv, and Mimic at any Modulation thanks to my collection of TSOP98200!
PWM Motor controlling via GPIO!

Stay tuned! 🙂