Support Statement: Shared Files (using gmsl)

A new approach to SharedFiles

This Support Statement documents a deprecated approach to Shared Files consolidation that uses a gmsl script to generate SharedFiles commands. We have developed a more sophisticated SharedFile algorithm in a stand-alone gmUtility, gmSharedFile.exe (C# using gmAPI).  The gmSL approach is deprecated.  The new approach is documented here.

I have a set of VBPs that share code files; that is, many VBPs reference the same code files. In default gmStudio Translations, the resulting .NET projects each gets separate copies of the shared files they reference. This file copying simplifies the translation, but it has some disadvantages in terms of version control, builds, and deployments. An alternative structure is to put the shared files into one of more "host" assemblies and have the translated application projects reference the shared files through the host assembly. Migrating to this assembly-based code sharing model is the approach described here.

For this example, all of the shared code files will be placed into a single new .NET DLL assembly project and a single new namespace. This "Single Dedicated Host" strategy is appropriate when the legacy code base lacks shared DLLs of its own and when there is no benefit to creating multiple host DLLs.

Step 1: Creating the Single Dedicated Host Project

The legacy system does not have any DLLs that could have shared files added, so a host DLL must be created from scratch.

First create an empty starter VBP manually adding references to the share code files and any required COM.

To get information needed to do this, I create a SharedFiles script that loads the legacy VBPs that share code:

<gmBasic>
<Storage Action="Create" Identifier="Shared" />
<select Target="%UserFolder%" />
<Select Local="%IdfFromCodeFolder%" />
<Select System="%IdfFromIdlFolder%" />
<Select Progress="1" />

<Load project="%VirtualRoot%\ABCView\ABCView.vbp" SourceCode="On" />
<Load project="%VirtualRoot%\ABCRead\ABCRead.vbp" SourceCode="On" />
<Load project="%VirtualRoot%\ABCManage\ABCManage.vbp" SourceCode="On" />
<Load project="%VirtualRoot%\ABCMonitor\ABCMonitor.vbp" SourceCode="On" />
<Load project="%VirtualRoot%\ABCReport\ABCReport.vbp" SourceCode="On" />
<Load project="%VirtualRoot%\ABCClock\ABCClock.vbp" SourceCode="On" />
<Load project="%VirtualRoot%\ABCEmailReport\ABCEmailReport.vbp" SourceCode="On" />
<Load project="%VirtualRoot%\ABCCheck\ABCCheck.vbp" SourceCode="On" />
<Load project="%VirtualRoot%\ABCKey\ABCKey.vbp" SourceCode="On" />
<Load project="%VirtualRoot%\ABCUpdate\ABCUpdate.vbp" SourceCode="On" />

<SharedFile />

<Storage Action="Close" />
</gmBasic>

Add script as a Custom task in the gmStudio project and process it with the Translate operation. The resulting log output contains a detailed report of code file sharing across the VBPs followed Registry-SharedFiles commands:

Basic Processor V31.13(08/02/20) System Build(08/02/20 4:21:15)
Processing file: \Applications\ABCCommon\ABCCommon.vbp
Processing file: \Applications\ABCView\ABCView.vbp
Processing file: \Applications\ABCRead\ABCRead.vbp
Processing file: \Applications\ABCManage\ABCManage.vbp
Processing file: \Applications\ABCMonitor\ABCMonitor.vbp
Processing file: \Applications\ABCReport\ABCReport.vbp
Processing file: \Applications\ABCClock\ABCClock.vbp
Processing file: \Applications\ABCEmailReport\ABCEmailReport.vbp
Processing file: \Applications\ABCCheck\ABCCheck.vbp
Processing file: \Applications\ABCKey\ABCKey.vbp
Processing file: \Applications\ABCUpdate\ABCUpdate.vbp
There were 44 Shared Files in this group of projects: Modules = 30, Forms = 8 Classes = 6
The Module file [\Applications\glbl-Monitor.bas] is shared by 11 projects
...
<Registry type="SharedFile" Source="\Applications\glbl-Monitor.bas" Target="\Applications\ABCView\ABCView.vbp;ABCView.exe" />
<Registry type="SharedFile" Source="\Applications\glbl-Monitor.cls" Target="\Applications\ABCView\ABCView.vbp;ABCView.exe" />
...
<Registry type="SharedFile" Source="\Applications\glbl-modShift.bas" Target="\Applications\ABCClock\ABCClock.vbp;ABCClock.exe" />
<Registry type="SharedFile" Source="\Applications\glbl-modLocalPort.bas" Target="\Applications\ABCClock\ABCClock.vbp;ABCClock.exe" />

Note: this process does not detect code clones (i.e. independent files with identical content). If you want to address clones, all sharing VBPs must be modified to point to one of the clone copies.

The listing generated by the SharedFilesPrep script reveals both the shared files and potential VBPs that might "host" them. However, in this case, these are EXE projects that are not suitable as shared hosts. This listing shows the majority of SharedFiles end up in ABCView. I will use the ABCView.vbp as the starter for my new Dedicated Host VBP just to get most of the COM references required.

The new VBP is modified to have the name ABCCommon, inside and out. Several VBP attributes are changed: Type=OleDll and Startup="(None)".
The new VBP must reference all of the shared code files as well the unique set of COM file references (ocx, tlb, dll) found in the VBPs above: ABCView, METRead, ABCManage, and ABCClock.

The new VBP may not build in VB6 depending on environmental factors and that is not a requirement. However, the VBP must translate with gmStudio giving a Host project: ABCCommon.csproj that builds in .NET.

Step 2: Translating the legacy code to .NET codes that use the Host Assembly

Step 2a. Generate the Registry-Shared Files commands:

A Load command is added to the top of the list of Load commands in the SharedFilesPrep script:

<Load project="%VirtualRoot%\ABCCommon\ABCCommon.vbp" SourceCode="On" />

Running SharedFilesPrep.xml gives a listing of Registry-SharedFiles commands all having Target=ABCCommon.

<Registry type="SharedFile" Source="\Applications\glbl-Monitor.bas" Target="\Applications\ABCCommon\ABCCommon.vbp;ABCCommon.dll" />
<Registry type="SharedFile" Source="\Applications\glbl-Monitor.cls" Target="\Applications\ABCCommon\ABCCommon.vbp;ABCCommon.dll" />
... 
<Registry type="SharedFile" Source="\Applications\glbl-modABCMail.bas" Target="\Applications\ABCCommon\ABCCommon.vbp;ABCCommon.dll" />
<Registry type="SharedFile" Source="\Applications\glbl-modLocalPort.bas" Target="\Applications\ABCCommon\ABCCommon.vbp;ABCCommon.dll" />


This is as expected and it shows we created the shared host VBP properly.

Step 2b. Add the Registry-SharedFiles commands into a GlobalSettings file

Copy the Registry-SharedFiles commands from the log of SharedFilesPrep to a GlobalSettings file.
Translate the GlobalSettings file creating a GlobalSettings.vbi file.

Note: A code base with many shared files is likely to already have a GlobalSettings file containing EditFile and RefactorFiles. The registry commands may be placed in a block at the top or bottom of this file.

Step 2c. Activate the GlobalSettings and SharedFiles migration

<Select GlobalSettings="%UserFolder%\GlobalSettings" />
<Select SharedFile="on" />

Note: The Select GlobalSettings command may already be present. A second one should not be added.

Step 2d. Run the Translations

Step 3. Verify Results

The SharedFiles migration causes the following types of changes in the generated codes:

1) The IDFs generated for the shared host VBPs will contain declarations for all public elements of files registered as SharedFiles.
2) The translation logs of VBPs that contained shared files will report loading the IDFs for the SharedFiles hosts.
3) The translations of VBPs that contained shared files will reference SharedFiles host assemblies.
4) References to SharedFiles host API elements will be done through the SharedFiles namespaces.

Note: for this example, there is a single SharedFile host and namespace: ABCCommon.

Step 4. Put it all together in a Visual Studio solution

Step 4a. Add the csproj for the host assembly to the GenerateSolution.txt file for the system

:: Description: VS Solution Project List
[Folder] App
%DeployFolder%\ABCCommon\ABCCommon.csproj
%DeployFolder%\ABCCheck\ABCCheck.csproj
%DeployFolder%\ABCClock\ABCClock.csproj
%DeployFolder%\ABCEmailReport\ABCEmailReport.csproj
%DeployFolder%\ABCKey\ABCKey.csproj
%DeployFolder%\ABCManage\ABCManage.csproj
%DeployFolder%\ABCMonitor\ABCMonitor.csproj
%DeployFolder%\ABCRead\ABCRead.csproj
%DeployFolder%\ABCReport\ABCReport.csproj
%DeployFolder%\ABCUpdate\ABCUpdate.csproj
%DeployFolder%\ABCView\ABCView.csproj
[Folder] Support
%DeployFolder%\MigrationSupport\MigrationSupport.csproj
%DeployFolder%\AccessRotateText\AccessRotateText.csproj
%DeployFolder%\ADODB\ADODB.csproj
%DeployFolder%\ccrFileDialogs6\ccrFileDialogs6.csproj
%DeployFolder%\CCRPDTP6\CCRPDTP6.csproj
%DeployFolder%\CCRProgressBar6\CCRProgressBar6.csproj
%DeployFolder%\ccrpTimers6\ccrpTimers6.csproj
%DeployFolder%\cmbSearch\cmbSearch.csproj
%DeployFolder%\CPShapeL\CPShapeL.csproj
%DeployFolder%\MSComCtl2\MSComCtl2.csproj
%DeployFolder%\MSComctlLib\MSComctlLib.csproj
%DeployFolder%\MSComDlg\MSComDlg.csproj
%DeployFolder%\MSWinsockLib\MSWinsockLib.csproj
%DeployFolder%\ODCboLst6\ODCboLst6.csproj
%DeployFolder%\OSPOP3_Plus\OSPOP3_Plus.csproj
%DeployFolder%\OSSMTP_Plus\OSSMTP_Plus.csproj
%DeployFolder%\PicClip\PicClip.csproj
%DeployFolder%\SCOMLib\SCOMLib.csproj
%DeployFolder%\Scripting\Scripting.csproj
%DeployFolder%\Shell32\Shell32.csproj
%DeployFolder%\SSubTimer6\SSubTimer6.csproj
%DeployFolder%\TabDlg\TabDlg.csproj
%DeployFolder%\vb6projectSocketMax\vb6projectSocketMax.csproj
%DeployFolder%\vbAcceleratorSGrid6\vbAcceleratorSGrid6.csproj
%DeployFolder%\VBRUN\VBRUN.csproj

Step 4b. Use gmStudio to generate the full integrated Visual Studio solution with projects cross-referencing each other.

The build model now contains a new dependency on the Shared host assembly and references to it must be made properly throughout the rest of the translations.

Step 4c. Build the VS solution to test the integration of the Shared Host assembly