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.
Visual Basic 6 provides a mechanism to invoke methods in native code via dynamic-linked libraries (DLLs). This mechanism is possible to be used with the DECLARE statement in VB6. This statement indicates to the VB6 compiler where the procedure is located, how it is identified, its calling sequence and return type, and the string character set it uses. The Declare statement creates a reference to an external procedure and supplies this necessary information. At this way the external procedure is called as any other method in the user code.
The following is typical declaration and invocation of a Windows API method:
Declare Function getUserName Lib "advapi32.dll" Alias "GetUserNameA" (
ByVal lpBuffer As String, ByRef nSize As Integer) As Integer
Dim buffer As String = New String(CChar(" "), 25)
Dim retVal As Integer = getUserName(buffer, 25)
Dim userName As String = Strings.Left(buffer, InStr(buffer, Chr(0)) - 1)
In this case getUser internal method displays the current user name, to do this the GetUserNameA external method is being exposed from advapi32.dll with its respective parameters and return value.
Platform Invocation Service (PInvoke) is a feature of Common Language Infrastructure implementations, like Microsoft Common Language Runtime, that enables manage code to call native code. This is the mechanism provided by .NET to invoke this kind of methods.
This is an example of how the Beep external method is declared as PInvoke method, which could be used in anywhere of the user code:
static extern Boolean MessageBeep(UInt32 beepType);
The PInvoke infrastructure supplied by .NET is a mechanism for programmers in .NET to invoke external methods in an efficient and a strong way. It will provide a .NET styled syntax to expose external methods and a natural way to invoke those external methods.
In general for every external *.dll call, the VBUC generates the required interoperability data-type marshaling to allow a smooth interaction between.NET and unmanaged code. The most common changes in this feature are:
- Adds specific interoperability attributes to the signatures of upgraded API calls
- Adds error handling for the upgraded API calls.
- Generates the correct data types for pointer-type parameters
- Modifies structure generation so the marshaling of structs to Windows API calls and the copying of structures in migrated .NET applications is completely automated.
Sometimes declarations (or exposition) of external method in .NET is too easy, but invocation is sometimes complex because it has to deal with error handling, marshaling data types and setting back values to its respective variables. This code seems to be larger and not natural so that the VBUC applies some refactoring to centralize all this code in methods that will contain all code related with those issues.
In this way user code will be presented very similar than original, with no additional code to support marshaling nor error handling, and this other code will be centralized in some files where user can improve it or change it if necessary. Maintainability and remove duplicated code are the main reasons why this approach is implemented at this way.
In addition, as above says, many code for handling error, marshaling data types, specially if struct types are used to send or for setting back return values to variables, must be generated in order to get the same behavior as VB6 had. This all code is generated by VBUC in those centralized files in a transparent way for all needed cases, mainly bitable types; and it will log EWIs in cases where some manual revision should be done, for not bitable types.