- INDEX VB TO NET
- KNOWLEDGE BASE
- EWIS-ISSUES
- ISSUE #1040
ISSUE #1040
%1 function is not supported
Description
This is a General EWI, and as such it can be emitted for several reason. These are but three of more common cases for this EWI but there can be other code variations that may cause it to appear, especially when using undocumented or legacy functions in Visual Basic 6.
Undocumented Pointer Functions
Visual Basic 6 includes several of undocumented functions like: ObjPtr, StrPtr, and VarPtr to name a few. These functions, in particular, return the memory address of the location where the corresponding object, variable, or string is stored. This memory address, also known as a pointer, can then be passed to Windows APIs that accept memory addresses as parameters.
These functions are no longer supported in the .NET Framework; however, most of the scenarios where it was used can be addressed through new functionality in the .NET Framework. Many of the Windows API calls that were necessary in Visual Basic 6.0 are now encapsulated in the framework; pointer references are no longer necessary.
IsMissing function
In Visual Basic 6 the IsMissing function was used to test for optional arguments passed to user-defined function procedures as variants. Default values were not required for optional arguments; if no value was passed then IsMissing returned true. It was also possible to specify a default value of a primitive type to pass to the Variant argument.
RecordSet functions
Some RecordSet functions like MoveFirst, MoveLast are no longer supported, since RecordSets are migrated to DataSets which are not sequential read data structures.
Recommendations
General Recommendations
Somethimes the functionality provided by the not supported function is no longer necesary.
For example, the MoveFirst funciton in a RecordSet object. RecordSets are migrated to DataSet objects, which are not sequential readers and instead use index-based access. The underlying code will require some changes, but the MoveFirst function is no longer necesary.
Research the behavior of the no longer supported function. In some cases a .Net alternative may exist. In other cases it may be necessary to create a wrapper or inherit from a .Net class to ensure equivalent functionality.
Undocumented Pointer Functions
The System.Runtime.InteropServices namespace provides ample functionality to replace unmanaged pointer code with managed code. In most cases this namespace's objects can provide the desired functionality.
Things to keep in mind:
The .Net CLR makes no guarantee on the location of a managed object's address remaining static throughout the application's lifetime, due to Garbage Collection. The Garbage Collector is free to move objects around at any moment, hence why pointer logic in .Net can be troublesome if not handled properly.
To avoid having an object moved, lock the address of the variable to safely obtain its address. In Visual Basic.Net this is accomplished by calling the following code:
Dim ObjectHandle as GCHandle = GCHandle.Alloc(<ObjectRef>, GCHandleType.Pinned)
Dim ObjectAddress as IntPtr = ObjectHandle.AddrOfPinnedObject()
Once finished, the address can be unlocked by: ObjectHandle.Free()
In C# the fixed statement is used.
IsMissing function
In Visual Basic.Net the IsMissing function has been replaced by IsNothing, yet there might still be differences in behavior. To ensure functional equivalence modifications to the calling call might be necessary as outlined in the MSDN article.
For C#, since there are no optional parameters (C# 3.5 or earlier, however this feature is expected in C# 4.0) the only viable alternative is to compare reference types to null (mimicking th IsNothing function) and create overloads of the necessary methods for when certain parameters are missing.
Sample VB6
PrivateSub Command1_Click()
CheckedParameterSub
CheckedParameterSub "sent value"
CopyStringsWithPointers
EndSub
PrivateSub CheckedParameterSub(Optional myParam AsVariant)
If IsMissing(myParam) Then
MsgBox ("Parameter is missing!")
Else
If myParam IsNothingThen
MsgBox ("Parameter is Nothing")
Else
MsgBox ("Parameter: " + myParam)
EndIf
EndIf
EndSub
PrivateDeclareSub CopyMemory Lib"kernel32"Alias"RtlMoveMemory" _
(ByVal lpDest AsLong, ByVal lpSource AsLong, ByVal cbCopy AsLong)
PrivateSub CopyStringsWithPointers()
Dim sourceString AsString, destString AsString
sourceString = "Hello"
destString = " World"
CopyMemory ByVal StrPtr(destString), ByVal StrPtr(sourceString), LenB(sourceString)
MsgBox destString
EndSub
Output:
Message box with: Parameter is missing!
Message box with: Parameter is Nothing
Message box with: Paramter: sent value
Message box with: Hello World
Target VB.NET
PrivateDeclareSub CopyMemory Lib"kernel32" Alias"RtlMoveMemory"(ByVal lpDest AsInteger, ByVal lpSource AsInteger, ByVal cbCopy AsInteger)
PrivateSub Command1_Click(ByVal eventSender AsObject, ByVal eventArgs As EventArgs) Handles Command1.Click
Dim x As Collection
CheckedParameterSub()
x = Nothing
CheckedParameterSub(x)
x = New Collection
Set x = New Collection()
'UPGRADE_ISSUE: (1010) The preceding line couldn't be parsed.
CheckedParameterSub(x)
CopyStringsWithPointers()
EndSub
PrivateSub CheckedParameterSub(OptionalByRef myParam As Collection = Nothing)
'UPGRADE_ISSUE: (1040) IsMissing function is not supported.
If IsMissing(myParam) Then
MessageBox.Show("Parameter is missing!", Application.ProductName)
Else
If myParam IsNothingThen
MessageBox.Show("Parameter is Nothing", Application.ProductName)
Else
MessageBox.Show(CStr(CDbl("Parameter: ") + myParam.Item), Application.ProductName)
EndIf
EndIf
EndSub
PrivateSub CopyStringsWithPointers()
Dim sourceString AsString = "Hello"
Dim destString AsString = " World"
'UPGRADE_WARNING: (1041) LenB has a new behavior.
'UPGRADE_ISSUE: (1040) StrPtr function is not supported. CopyMemory(UpgradeStubs.VBA__HiddenModule.StrPtr(destString),UpgradeStubs.VBA__HiddenModule.StrPtr(sourceString),Encoding.Unicode.GetByteCount(sourceString))
MessageBox.Show(destString, Application.ProductName)
EndSub
Expected VB.NET
In this case, we've taken a worst case scenario for CheckedParameterSub which not only behaves differently for missing parameters but also for parameters that are set to Nothing. This means we have three possible outcomes from this sub.
However, in Visual Basic.NET due to obligatory default values for optional parameters we have no way of knowing if the caller did not specify any parameters! So in order to replicate this Visual Basic functionality in .Net we'll have to rely on creating a separate method that encapsulates the functionality previously reserved for when the parameter is missing.
For the pointer manipulation code we simply follow the recommendations outlined above.
Imports System.Runtime.InteropServices
PrivateDeclareSub CopyMemory Lib"kernel32" Alias"RtlMoveMemory"(ByVal lpDest AsInteger, ByVal lpSource AsInteger, ByVal cbCopy AsInteger)
PrivateSub Command1_Click(ByVal eventSender AsObject, ByVal eventArgs As EventArgs) Handles Command1.Click
Dim x As Collection
' Replace method call
CheckedParameterSubEmpty()
x = Nothing
CheckedParameterSub(x)
x = New Collection()
x.Add("sent value")
CheckedParameterSub(x)
CopyStringsWithPointers()
EndSub
PrivateSub CheckedParameterSubEmpty()
MessageBox.Show("Parameter is missing!", Application.ProductName)
EndSub
PrivateSub CheckedParameterSub(OptionalByRef myParam As Collection = Nothing)
'UPGRADE_ISSUE: (1040) IsMissing function is not supported.
If IsNothing(myParam) Then
MessageBox.Show("Parameter is Nothing", Application.ProductName)
Else
MessageBox.Show("Parameter: " + myParam.Item(1), Application.ProductName)
EndIf
EndSub
PrivateSub CopyStringsWithPointers()
Dim sourceString AsString = "Hello"
Dim destString AsString = " World"
Dim sourceGCHandle As GCHandle = GCHandle.Alloc(sourceString, GCHandleType.Pinned)
Dim sourceAddress AsInteger = sourceGCHandle.AddrOfPinnedObject.ToInt32
Dim destGCHandle As GCHandle = GCHandle.Alloc(destString, GCHandleType.Pinned)
Dim destAddress AsInteger = destGCHandle.AddrOfPinnedObject.ToInt32
CopyMemory(destAddress, sourceAddress, Encoding.Unicode.GetByteCount(sourceString))
sourceGCHandle.Free()
destGCHandle.Free()
MessageBox.Show(destString, Application.ProductName)
EndSub
Target C#
[DllImport("kernel32.dll")]
publicexternstaticvoid CopyMemory(int lpDest, int lpSource, int cbCopy);
privatevoid Command1_Click(Object eventSender, EventArgs eventArgs)
{
Collection x = null;
CheckedParameterSub();
x = null;
CheckedParameterSub(x);
x = newCollection();
//Set x = New Collection();
//UPGRADE_ISSUE: (1010) The preceding line couldn't be parsed.
CheckedParameterSub(x);
CopyStringsWithPointers();
}
privatevoid CheckedParameterSub(Collection myParam)
{
//UPGRADE_ISSUE: (1040) IsMissing function is not supported.
if (IsMissing(myParam))
{
MessageBox.Show("Parameter is missing!", Application.ProductName);
}
else
{
if (myParam == null)
{
MessageBox.Show("Parameter is Nothing", Application.ProductName);
}
else
{
MessageBox.Show((Double.Parse("Parameter: ") + Convert.ToDouble(myParam.Item)).ToString(), Application.ProductName);
}
}
}
privatevoid CheckedParameterSub()
{
CheckedParameterSub(null);
}
privatevoid CopyStringsWithPointers()
{
string sourceString = "Hello";
string destString = " World";
//UPGRADE_WARNING: (1041) LenB has a new behavior.
//UPGRADE_ISSUE: (1040) StrPtr function is not supported.
CopyMemory(UpgradeStubs.VBA__HiddenModule.StrPtr(destString),UpgradeStubs.VBA__HiddenModule.StrPtr(sourceString),Encoding.Unicode.GetByteCount(sourceString));
MessageBox.Show(destString, Application.ProductName);
}
Expected C#
In this case, we've taken a worst case scenario for CheckedParameterSub which not only behaves differently for missing parameters but also for parameters that are set to Nothing. This means we have three possible outcomes from this sub.
However, in C# we do not have optional parameters. It has been stated that this feature will be coming in C# 4.0, but until then we'll have to make due with overloads. So in order to replicate this Visual Basic functionality in C# we'll have to rely on the empty overload of CheckedParamaterSub to encapsulate the functionality previously reserved for when the parameter is missing. Fortunately, the VBUC will have already created this stub.
For the pointer manipulation code we simply follow the recommendations outlined above.
[DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
publicexternstaticvoid CopyMemory(int lpDest, int lpSource, int cbCopy);
privatevoid Command1_Click(Object eventSender, EventArgs eventArgs)
{
ArrayList x = null;
CheckedParameterSub();
x = null;
CheckedParameterSub(x);
x = newArrayList();
x.Add("sent value");
CheckedParameterSub(x);
CopyStringsWithPointers();
}
privatevoid CheckedParameterSub(ArrayList myParam)
{
if (myParam == null)
{
MessageBox.Show("Parameter is Nothing", Application.ProductName);
}
else
{
MessageBox.Show("Parameter: " + myParam[0], Application.ProductName);
}
}
privatevoid CheckedParameterSub()
{
MessageBox.Show("Parameter is missing!", Application.ProductName);
}
privatevoid CopyStringsWithPointers()
{
string sourceString = "Hello";
string destString = " World";
GCHandle sourceHandle = GCHandle.Alloc(sourceString, GCHandleType.Pinned);
int sourceAddress = sourceHandle.AddrOfPinnedObject().ToInt32();
GCHandle destHandle = GCHandle.Alloc(destString, GCHandleType.Pinned);
int destAddress = destHandle.AddrOfPinnedObject().ToInt32();
CopyMemory(destAddress, sourceAddress, Encoding.Unicode.GetByteCount(sourceString));
sourceHandle.Free();
destHandle.Free();
MessageBox.Show(destString, Application.ProductName);
}