Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 4 Next »

Introduction

The transition from weak to strong typing is a very important consideration for all large-scale upgrade efforts.   Generally speaking, VB6 will implicitly convert just about anything to anything.  Also VB6 has various weak types: Object, Variant, Form, Control, as well as a weakly typed collection class, that allow programmers to defer type checks until runtime.  Many COM APIs are exposed through weakly typed interfaces. To make matters worse, VB6 uses the same notation for a late call and an early call: the details are handled by VB6/COM runtime behind the scenes.  On the other hand, the .NET languages, tend to favor strong type checking at compile time, and making late calls in .NET typically requires deliberate use of object-oriented design, interfaces, or late call syntax.

In general, gmStudio will attempt to upgrade Variants, and to a lesser degree Objects, to strong types by analyzing usage of the weakly typed symbols and the collection of all types defined to the source system.  In most cases this produces cleaner, more correct, strongly typed code.  However usage information is insufficient or ambiguous, selecting a single strong type is not possible and  the upgraded code will retain the weaker types.  In some situations, the tool must use techniques like CallByName, dynamic, temporary variables, and casting to make the weakly-typed code build in .NET.  But, although the weakly-typed code builds, it may not run properly.    Furthermore, some VB6 applications use weakly typed code very deliberately to implement more dynamic logic.  When this is done, the upgraded code may need to be dynamic as well.

In order to help teams make the deal with with typing related upgrade issues,  gmStudio has many for implementing an upgrade solution that either uses stronger types or implements more dynamic logic, depending on what is required.  The commands for introducing stronger types are registry/fixtype, refactor/fixtype, modifying IDFs to have stronger types, and in some cases pre-edits to modify the original code.  The commands for allowing dynamic types are refactor/generic, refactor/overload, use of the dynamic metatype, dictionaryAdd/CollectionAdd, and many others. 

This article presents the techniques for improving how your upgrade solution handles weakly typed code.

ByRef object arguments taking with multiple types

 

of actual arguments to formal arguments, but C# does not.  This is one of t

Consider rvarItem the following example.  In the calling code, is used to return different types of variables from a weakly types collection:

Public Function g_Collection_ItemIsDefined(ByVal vobjCollection As Object, Optional ByVal vvarItem As Variant, Optional ByVal vstrKey As String, _
    Optional ByRef rvarItem As Variant) As Boolean

Furthermore, suppose g_Collection_ItemIsDefined is called with many different types of actual arguments passed to the rvarItem so the tool chooses to declare the formal argument as type object.  However, passing an argument by ref is in .NET requires strict typing and to build within this constraint, the tool must introduce a temporary variable to box the actual argument.  These are named argTemp1, argTetmp2... argTempN with numbering starting at 1 within each file.  For example, passing a boolean to a ref object looks like this:

object argTemp1 = blnOperationEnabled;
if (basCollection.g_Collection_ItemIsDefined(colItemOperations,null,strOperation,ref argTemp1))

By default the argTemp variable is ONLY initialized from the actual argument and used in the call. It is not moved back to the actual argument after the call. In some cases, ignoring the change to a ByRef argument is a breaking change from the VB6 that must be addressed using various techniques depending on the situation. One possible technique is to use the Refactor/Overload as described here.

Refactor/Overload is a translation script command (gmPL) command that may be be added to the Compile block in your translation script as in the following example:

...
<Compile Project="..." >
<Refactor>
<OverLoad Identifier="Project1.Form1.MySub.o_Var" Types="Boolean" />
</Refactor>
</Compile>
...
where
Identifier  is the fully qualified identifier of the argument to overload
Types       is a list of source types known to the tool.  This can include gmStudio's VB6 types, application types, and external COM types

The effect of adding a refactor/overload command is three-fold:

1) The tool will report warnings about other types used with the argument.  These warnings can be used to add more types ot the Types list. For example:

Warning#3005: Passing <App.frmClientGroupSelect.m_DisplayedClientGroupColumns_AdjustColumnHeaders.chdSortColumnHeader> with type <MSComctlLib.ColumnHeader> to <App.g_Collection_ItemIsDefined.rvarItem> with type <Variant>
Warning#3005: Passing <App.frmRecipeBookSelect.m_DisplayedRecipeBookColumns_AdjustColumnHeaders.chdSortColumnHeader> with type <MSComctlLib.ColumnHeader> to <App.g_Collection_ItemIsDefined.rvarItem> with type <Variant>
Warning#3005: Passing <App.frmFoodClassSelect.m_FoodClass_Populate.nodFoodClass> with type <MSComctlLib.Node> to <App.g_Collection_ItemIsDefined.rvarItem> with type <Variant>
Warning#3005: Passing <App.frmFoodClassSelect.m_FoodClass_PopulateChildren.nodParentFoodClass> with type <MSComctlLib.Node> to <App.g_Collection_ItemIsDefined.rvarItem> with type <Variant>
Warning#3005: Passing <App.frmClientSelect.m_Clients_Populate.lsiClient> with type <MSComctlLib.ListItem> to <App.g_Collection_ItemIsDefined.rvarItem> with type <Variant>
Warning#3005: Passing <App.frmClientSelect.m_DisplayedClientColumns_AdjustColumnHeaders.chdSortColumnHeader> with type <MSComctlLib.ColumnHeader> to <App.g_Collection_ItemIsDefined.rvarItem> with type <Variant>
....
Warning#3005: Passing <App.g_VBObject_SetEnabled.colContainerContents> with type <Collection> to <App.g_Collection_ItemIsDefined.rvarItem> with type <Variant>
Warning#3005: Passing <App.g_VBObjects_LoadContainers.colContainerContents> with type <Collection> to <App.g_Collection_ItemIsDefined.rvarItem> with type <Variant>
Warning#3005: Passing <App.g_DragCursor_Set.colItemTypeItems> with type <Collection> to <App.g_Collection_ItemIsDefined.rvarItem> with type <Variant>
Warning#3005: Passing <App.g_RecentItems_Add.strRecentItem> with type <String> to <App.g_Collection_ItemIsDefined.rvarItem> with type <Variant>

2) The tool will generated an overload of the method for each type in Types.  These overloads deal with marshalling different variables to the weakly typed formal argument in the implementation.  Notice that in the wrapper, the temporary arg is moved back actual arg after the call. For example:

      public static bool g_Collection_ItemIsDefined(object vobjCollection,object vvarItem,string vstrKey,ref bool rvarItem)
      {
         object TemporaryArg1 = rvarItem;
         bool retVal = g_Collection_ItemIsDefined(vobjCollection,vvarItem,vstrKey,ref TemporaryArg1);
         rvarItem = Convert.ToBoolean(TemporaryArg1);
         return retVal;
      }

3) The tool  removes the use of the ref tempArg from the calls where an actual arg of one of the types in Types is passed.  For example, the using argTemp1 shown above becomes simply:

if (basCollection.g_Collection_ItemIsDefined(colItemOperations,null,strOperation,ref blnOperationEnabled))

 

Refactor Generic

<Compile Project="..." >
<Refactor>
<Generic identifier="Project1.Form1.MySub.o_Var" Types="Integer,String" />
</Refactor>
</Compile>
 

 

 

  • No labels