The Microsoft Windows application programming interface (Win API) allows developers to call Windows OS functions directly. All programs interact with the Windows API at some point, either directly or through some other API.
Many system resources can be managed using this API, and applications employ them to perform special functions not available from the programming language natively, such as file system access, working with the Windows registry, or interacting directly with the user interface. These functions are implemented in several DLLs that reside on the Windows\system32 folder, including kernel32.dll, advapi32.dll, user32.dll, gdi32.dll, and others.
Visual Basic 6.0 is not the exception, and it provides the “declare” keyword to call methods from external libraries. These methods are not necessarily written in VB6. This keyword is used to access functionality from dlls part of both the Win API and external components.
A brief example of a declare keyword usage is:
Private Declare Sub SetWindowPos Lib "User32" (arguments…)
This statement declares a private subroutine with no return value called “SetWindowPos” in the VB6.0 class or module that belongs to the “User32” Win API library. As mentioned before, the “declare” keyword can be used to access external dlls other than those included in the Windows API.
In .NET, there are two concepts that need to be acknowledged to better understand external dll references. These are the concepts of managed and unmanaged code, which explain the main differences regarding the external dll use. The following paragraphs explain these differences:
The .NET platform introduced the Platform Invocation Services also known as “Pinvoke” to interact with unmanaged code from a managed environment. As for the code itself,.NET declarations for calls to external dlls are not that different from VB6 declarations. There is, however, an important difference – the way the input and output parameters are passed between both types of code require and special transformations known as marshalling.
Marshalling transforms the data type of input and output parameters from unmanaged code to .NET’s Common Type System, so they can be used as managed data types. This allows the CLR to handle unmanaged parameters in memory as if they were managed.
The .NET Pinvoke functionality often use composite data types, known as structures or structs, to transmit parameters to and from unmanaged code. Struct types require marshalling to enforce memory-safe execution on the managed side.
Starting with version 3.0, the VBUC is able to automatically upgrade most Windows API calls from the “declare” keyword to the correct P/invoke counterpart. For every external *.dll call, the VBUC generates the required interoperability data-type marshalling to allow a smooth interaction between.NET and unmanaged code. The most changes in this feature are:
The following code sample demonstrates how the VBUC upgrades Windows API calls to their corresponding Pinvoke signatures:
Private Declare Sub SetWindowPos Lib "User32" (ByVal hWnd As Integer, ByVal hWndInsertAfter As Integer, ByVal X As Integer, ByVal Y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal wFlags As Integer) Private Declare Function GetWindowLong Lib "User32" Alias "GetWindowLongA"(ByVal hWnd As Integer, ByVal nIndex As Integer) As Integer Private Declare Function SetWindowLong Lib "User32" Alias "SetWindowLongA"(ByVal hWnd As Integer, ByVal nIndex As Integer, ByVal dwNewLong As Integer) As Integer
Private Declare Sub SetWindowPos Lib "User32" (ByVal hWnd As Long, ByVal hWndInsertAfter As Long, ByVal X As Long, ByVal Y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) Private Declare Function GetWindowLong Lib "User32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long Private Declare Function SetWindowLong Lib "User32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
[DllImport("User32.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)] public extern static void SetWindowPos( int hWnd, int hWndInsertAfter, int X, int Y, int cx, int cy, int wFlags); [DllImport("User32.dll", EntryPoint = "GetWindowLongA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)] public extern static int GetWindowLong( int hWnd, int nIndex); [DllImport("User32.dll", EntryPoint = "SetWindowLongA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)] public extern static int SetWindowLong( int hWnd, int nIndex, int dwNewLong);
Note that the C# code features marshalling data (between brackets) used to interpret the input and output data from the windows API calls. Also note that VB.NET code doesn’t have this information, since is implicit for this language.