Tag Archives: vb6

Passing a small object in the querystring

For the current project at work I need to pass some information from an application or website to another website. Rather than storing the state in a database I decided to serialize the state object and pass it, encrypted, in the querystring.

This is the encoding function:


Dim plaintext as String = myState.Serialize
Dim bytes As Byte() = ASCIIEncoding.UTF8.GetBytes(plaintext)
Dim encrypted As Byte() = Encryption.Encrypt(bytes, key)
Dim encodedtext As String = Convert.ToBase64String(encrypted)
Return System.Web.HttpUtility.UrlEncode(encodedtext)

Note that the serialized and encrypted object is url encoded using the System.Web.HttpUtility.

This is the decode function:


Dim decodedtext As Byte() = Convert.FromBase64String(querystring)
Dim decrypted As Byte() = Encryption.Decrypt(decodedtext, key)
Dim plaintext As String = ASCIIEncoding.UTF8.GetString(decrypted)
Dim mystate As New State
mystate.Deserialize(plaintext)
Return mystate

and in C# because I need to practice my skills:


byte[] decodedtext = Convert.FromBase64String(querystring);
byte[] decrypted = Encryption.Decrypt(decodedtext, key);
string plaintext = ASCIIEncoding.UTF8.GetString(decrypted);
State mystate = new State();
mystate.Deserialize(plaintext);
return mystate;

Note that the querystring is not url decoded, this is because the ASP.NET http handler decodes the querystring before it is passed to your code.

Eldritch Coding Horrors, Part 1

I found this terror today in a legacy application.

Program A uses SetWindowLong to hook the message loop for a Windows Form:


Friend Function StartListening() As Boolean
SetWindowLong(m_MappedWndHandle, GWL_WNDPROC, AddressOf WindowProcedure)
End Function

Program A then shells program B. Program B sends a WM_COPYDATA message to program A. Program A intercept the message, reads the data, and then passes the message to the form:


Public Function WindowProcedure(ByVal hwnd As Long, ByVal msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
If msg = WM_COPYDATA Then
objMapMemoryData.ReadMessage lParam
End If
WindowProcedure = CallWindowProc(objMapMemoryData.GetWindowProcedureAddress, hwnd, msg, wParam, lParam)
End Function

objMapMemoryData.ReadMessage raises an event that is handled by the Form:


Friend Function ReadMessage(ByVal lParam As Long) As Boolean
‘SNIP: Copy memory around
RaiseEvent MapMemoryDataEvent
End Function

Private Sub objMapMemoryData_MapMemoryDataEvent()
‘SNIP: Process event here
End Sub

To Bind or Not To Bind That is the Question

One of the things that VB6 is very good at is late binding. Back in the days before .NET late binding or OLE Automation as it was also known was one of the only ways to do certain tasks such as create reports in Excel, run Word mail merges from databases, or print nicely formatted documents. However there were downsides too. Applications that late bound to DLLs and EXEs had no means to ensure that the target supported the call being made or even that the target was installed on the PC. This allowed difficult to debug problems to creep into your program.

 So the best solution to late binding problems was to early bind to any DLLs that you wanted to use. This way you could ensure that the interface was correct and that the DLL was installed on the system. However this led to another problem: DLL Hell. When an updated DLL was installed your system had to keep track of each version of the DLL and make sure that the right program called the right version. If a DLL was relied upon by a lot of programs and was regularly updated then a new installation could mess up an old program.

 So .NET included ‘xcopy’ installation – simply include the right version of each referenced DLL in the same directory as the application and late binding became something that only ‘bad’ programmers did. Early bound DLLs also need to be loaded by the system when your program initiates.

 The thing is that late binding solves a certain class of problem, one that often occurs in legacy applications. When adding new features supported by a DLL or refactoring old features to use new DLLs early binding to the DLL can cause problems with your installation program/routine, especially where DLL Hell issues have been previously overcome or there are a large number of support DLLs required for the application.

 So a simple approach to enhancing or refactoring an old application is to develop with the references early bound and then switch to late binding and copy the required DLLs into either the application directory or another suitable location.

Utilising a .NET DLL with a .config file in VB6

This isn’t great practice but it’s sometimes necessary to use a config file for a DLL developed in .NET that then needs to be called from VB6.

To debug a VB6 app/exe that uses a .NET dll with a .config file you must create a vb6.exe.config file in the same folder as the VB6 executable. Typically this is C:\Program Files\Microsoft Visual Studio\VB98.

When the VB6 dll/exe is compiled the config file must be named the same as the dll/exe with a .config extension and be placed in the same location as the dll/exe. e.g. MyVB6Program.exe.config

The .NET DLL can use code like this to retrieve, for instance, connection strings:

'Fetch the connection string from the app.config file
Dim settings As ConnectionStringSettings = ConfigurationManager.ConnectionStrings(name)
If settings IsNot Nothing Then
' Retrieve the partial connection string.
Dim connectString As String = settings.ConnectionString
' Create a new SqlConnectionStringBuilder based on the partial connection string retrieved from the config file.
Dim builder As DbConnectionStringBuilder
'We need to act differently depending on wether we are using Access or SQL
If settings.ProviderName.ToUpper = "SYSTEM.DATA.ODBC" Then
builder = New OdbcConnectionStringBuilder(connectString)
' Supply the additional values.
builder.Add("Uid", "USER_NAME_HERE")
builder.Add("Pwd", "PASSWORD_HERE")
'This is a bit of a hack but we can check the odbc connection string to see if it contains the phrase 'SQL Server'
'as all sql server odbc connection strings up to and including version 11 will contain this phrase
If settings.ConnectionString.IndexOf("SQL Server", 0, StringComparison.CurrentCultureIgnoreCase) <> -1 Then
useSQLServer = True 'Set the useSQLServer flag
Else
Throw New Exception("Provider '" & settings.ProviderName & "' is not allowed.")
End If
Return builder.ConnectionString
Else
Throw New Exception("Unable to retrieve app.config connectionstrings.")
End If

Use WMI to find and terminate a process by it’s EXE filename

This function will take a filepath to an executable, which can be discovered from the registry, and terminate any process using that executable.

Public Function WMICloseProgram(ByVal fileName As String) As Boolean
 On Error GoTo ErrorHandler
 Dim wmi As Object
 Set wmi = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
 'Use winmgmts to access full process list
 fileName = Replace(fileName, "\", "\\")
 Dim query As String
 query = "Select * from Win32_Process Where ExecutablePath = '" + fileName + "'"
 Dim results As Object
 Set results = wmi.ExecQuery(query)
 If results.Count > 0 Then
    'At least one matching result
    Dim process As Object
    For Each process In results
       process.Terminate
    Next
 WMICloseProgram = True
 End If
 ErrorHandler:
 If (Err.Number <> 0) Then WMICloseProgram = False
 Set wmi = Nothing
End Function

Get a future proof user friendly operating system version in VB6

To get a user friendly, future proof, operating system version in VB6 code use the WMI classes like this:

Public Function GetFriendlyOSVersion() As String
        Dim query As String
        query = "SELECT Caption FROM Win32_OperatingSystem"
        Dim results As Object
        Set results = GetObject("Winmgmts:").ExecQuery(query)
        Dim info As Object
        For Each info In results
            GetFriendlyOSVersion = info.Caption
        Next info
    End Function