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);
    }


    Download VBUC Free Trial
    Download VBUC Now

    It's time to eradicate VB6
    ROI of eradicating VB6

    8 Proven Tips for
    Planning a Successful Migration

    8 Tips for migration