Home > Blockchain >  ArrayList marshalling and useLegacyV2RuntimeActivationPolicy
ArrayList marshalling and useLegacyV2RuntimeActivationPolicy

Time:09-30

I've noticed that if I have a .NET Framework 4.8 assembly that exposes a COM interop method that takes a System.Collections.ArrayList parameter, then call that method from VB6, the .NET method gets a copy of the ArrayList, rather than the original ArrayList that was passed: GetHashCode() returns different values on the VB6 object vs. the .NET object, and if the .NET method modifies the ArrayList, the modifications aren't made to the object back on the VB6 side.

However, if in the .exe.config file, I add a <startup useLegacyV2RuntimeActivationPolicy="true"> section, it works as expected: GetHashCode() returns the same value on both the VB6 and .NET sides, and modifications made by the .NET method are seen on the VB6 side.

Why does this happen? Is there a way to get it to work the way I expect without setting useLegacyV2RuntimeActivationPolicy="true"? (I'm fine with setting it to true; I'm mainly curious why that needs to be done for this to work)

Details:

I have this in the .NET side, with "Register for COM interop" checked on the project build configuration.

using System.Collections;
using System.Runtime.InteropServices;

[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class ItemAdder
{
    public int AddItem(ArrayList ar)
    {
        ar.Add("def");
        return ar.GetHashCode();
    }
}

On the VB6 side, create project, add a project reference to C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.tlb, and put this in a Module:

Option Explicit

Sub Main()
  Dim al As New ArrayList
  Dim adder As New ItemAdder
  Dim msg As String
  
  al.Add "abc"
  msg = "HashCode in VB6 is: " & al.GetHashCode() & vbCrLf
  msg = msg & "HashCode in .NET is: " & adder.AddItem(al) & vbCrLf
  msg = msg & "Item was " & IIf(al.Count <= 1, "not ", "") & "added"
  MsgBox msg
End Sub

And the .exe.config file (could use this as VB6.exe.config if running from the VB6 IDE, or if the VB6 project is compiled to an EXE, use it as that EXE's .config file):

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <startup useLegacyV2RuntimeActivationPolicy="true">
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
  </startup>
</configuration>

If useLegacyV2RuntimeActivationPolicy="true", the MsgBox shows both HashCodes as equal, and that the item was added. If useLegacyV2RuntimeActivationPolicy="false", the MsgBox shows different HashCodes, and that the item was not added.

CodePudding user response:

This is because enter image description here

ArrayList COM registration in 32-bit (on a 64-bit system), in mscorlib.dll built on .NET Framework 1: enter image description here

So, if you build your .dll with .NET Framework 4, and call it from VB6 (which is 32-bit), you'll mix mscorlib in different versions (4 and 1 here) in the same process, which creates issues.

As stated in official documentation on <supportedRuntime> element

If your application uses legacy activation paths [...] and you want those paths to activate version 4 of the CLR instead of an earlier version, or if your application is built with the .NET Framework 4 but has a dependency on a mixed-mode assembly built with an earlier version of the .NET Framework, it is not sufficient to specify the .NET Framework 4 in the list of supported runtimes. In addition, in the element in your configuration file, you must set the useLegacyV2RuntimeActivationPolicy attribute to true. [...]

This is a basically "magic" switch, as explained here: What is useLegacyV2RuntimeActivationPolicy for?

The useLegacyV2RuntimeActivationPolicy attribute basically lets you say, “I have some dependencies on the legacy shim APIs. Please make them work the way they used to with respect to the chosen runtime.

Also from the same link:

Ultimately, this attribute has to do with the behavior of the “legacy shim APIs”. You can think of these as encompassing several categories of CLR activation:

[...]

Pre-v4 COM activation – This includes CoCreateInstance of a CLSID (or type identifier) whose latest registration is against a pre-v4 runtime version. Note this includes both the “new” operator on such a co-class from managed code, or the result of Activator.CreateInstance against a type created by Type.GetTypeFromCLSID on such a CLSID.

Pre-v4 IJW (mixed mode) activation – For example, calling into a native export on such an assembly

Native activation of a native runtime-provided COM CLSID – Such as CoCreateInstance on ICLRRuntimeHost’s CLSID

Native activation of a managed framework CLSID – Such as CoCreateInstance on System.ArrayList’s CLSID (extremely rare)

  • Related