VB Migration Partner

KNOWLEDGE BASE - Language


Previous | Index | Next 

[HOWTO] Migrate VB6 applications that use window subclassing

Even though VB Migration Partner can convert VB6 code that uses window subclassing (with some manual fixes on your part), you’d better off using one of the helper classes that the CodeArchitects.VBLibrary provides you with, the VB6WindowSubclasser class. This class enables you to implement subclassing in a simple and safe way and, just as important, without calling unmanaged code. Consider the following VB6 example that uses window subclassing:

' Some message constants.
Const WM_MOVE = &H3
Const WM_SIZING = &H214
Const WM_ACTIVATEAPP = &H1C

Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" ( _
        ByVal hWnd As Long, ByVal ndx As Long, ByVal newValue As Long) As Long
Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" ( _
        ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, _
        ByVal wParam As Long, ByVal lParam As Long) As Long
' This is used with the SetWindowLong API function.
Const GWL_WNDPROC = -4

Dim saveHWnd As Long        ' The handle of the subclassed window.
Dim oldProcAddr As Long     ' The address of the original window procedure

Sub StartSubclassing(ByVal hWnd As Long)
    saveHWnd = hWnd
    oldProcAddr = SetWindowLong(hWnd, GWL_WNDPROC, AddressOf WndProc)
End Sub

Sub StopSubclassing()
    SetWindowLong saveHWnd, GWL_WNDPROC, oldProcAddr
End Sub

Function WndProc(ByVal hWnd As Long, ByVal uMsg As Long, _
        ByVal wParam As Long, ByVal lParam As Long) As Long
    ' Send the message to the original window procedure, and then
    ' return Windows the return value from the original procedure.
    WndProc = CallWindowProc(oldProcAddr, hWnd, uMsg, wParam, lParam)
    
    ' process the message
    Select Case uMsg
        ' (omitted)
    End Select
End Function

The VB6WindowSubclasser’s constructor takes the handle of the window to be subclassed and a delegate to a method that handles incoming messages (WndProc in this case). You can stop subclassing by invoking the VB6WindowSubclasser.Dispose method. Thus, the following VB.NET code is equivalent to the previous VB6 code:

Dim subclasser As VB6WindowSubclasser

Sub StartSubclassing(ByVal hWnd As Long)
    subclasser = New VB6WindowSubclasser(hWnd, AddressOf WndProc)
End Sub

Sub StopSubclassing()
    subclasser.Dispose()
End Sub

The code inside the WndProc method is same as before, except that you invoke the original window procedure by calling the VB6WindowSubclasser.CallWindowProc method:

Function WndProc(ByVal hWnd As Long, ByVal uMsg As Long, _
        ByVal wParam As Long, ByVal lParam As Long) As Long
    ' Send the message to the original window procedure, and prepare to 
    ' return Windows the return value from the original procedure.
    WndProc = subclasser.CallWindowProc(hWnd, uMsg, wParam, lParam)
    
    ' process the message
    Select Case uMsg
        ' (omitted)
    End Select
End Function

You can implement this technique by editing the migrated VB.NET code or, even better, you can by adding some pragmas to the original VB6 code:

' Some message constants.
Const WM_MOVE = &H3
Const WM_SIZING = &H214
Const WM_ACTIVATEAPP = &H1C

'## ParseMode Off
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" ( _
        ByVal hWnd As Long, ByVal ndx As Long, ByVal newValue As Long) As Long
Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" ( _
        ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, _
        ByVal wParam As Long, ByVal lParam As Long) As Long
' This is used with the SetWindowLong API function.
Const GWL_WNDPROC = -4

Dim saveHWnd As Long        ' The handle of the subclassed window.
Dim oldProcAddr As Long     ' The address of the original window procedure
'## ParseMode On
'## InsertStatement Dim subclasser As VB6WindowSubclasser

Sub StartSubclassing(ByVal hWnd As Long)
    '## ParseMode Off, 2
    saveHWnd = hWnd
    oldProcAddr = SetWindowLong(hWnd, GWL_WNDPROC, AddressOf WndProc)
    '## InsertStatement subclasser = New VB6WindowSubclasser(hWnd, AddressOf WndProc)
End Sub

Sub StopSubclassing()
    '## ReplaceStatement subclasser.Dispose()
    SetWindowLong saveHWnd, GWL_WNDPROC, oldProcAddr
End Sub

'## WndProc.MarkAsReferenced
Function WndProc(ByVal hWnd As Long, ByVal uMsg As Long, _
        ByVal wParam As Long, ByVal lParam As Long) As Long
    ' Send the message to the original window procedure, and then
    ' return Windows the return value from the original procedure.
    '## ReplaceStatement WndProc = subclasser.CallWindowProc(hWnd, uMsg, wParam, lParam)
    WndProc = CallWindowProc(oldProcAddr, hWnd, uMsg, wParam, lParam)
    
    ' process the message
    Select Case uMsg
        ' (omitted)
    End Select
End Function
Previous | Index | Next 




Follow Francesco Balena on VB6 migration’s group on

LinkedIn





Read Microsoft Corp’s official case study of a VB6 conversion using VB Migration Partner.




Code Architects and its partners offers remote and onsite migration services.

More details




Subscribe to our free newsletter for useful VB6 migration tips and techniques.

newsletter



To learn more about your VB6 applications, run VB6 Analyzer on your source code and send us the generated text file. You will receive a detailed report about your VB6 applications and how VB Migration Partner can help you to quickly and effectively migrate it to .NET.

Get free advice



A fully-working, time-limited Trial Edition of VB Migration Partner allows you to test it against your actual code

Get the Trial




The price of VB Migration Partner depends on the size of the VB6 application, the type of license, and other factors

Request a quote




Migrating a VB6 application in 10 easy steps

Comparing VB Migration Partner with Upgrade Wizard

Migration tools: Feature Comparison Table

All whitepapers