See the question and my original answer on StackOverflow

There are complex ways with COM, CLSIDs, IIDS, registry registration, proxies, interfaces, the whole shebang... but I will show you an easier way using the Running Object Table ("ROT")

Here is a sample WPF app that contains all what you need, pure C# code, no registration needed.

The app registers your live object in the ROT. You can check it's there using this ROTVIEW tool (run it as x64 if your process/OS is x64)

This is how your live object registration appears

enter image description here

Here is the VBScript to call it (make sure the WPF app is running)

Set app = GetObject("x:\somepath")
WScript.Echo app.ToUpperCase("hello")

The path here is artificial it doesn't need to point to a real file, it's just so VBScript can pick it. Outputs:

enter image description here

And here is the WPF app code:

using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Windows;
using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;

namespace MyWpfApp
{
    public partial class MainWindow : Window
    {
        private readonly int _cookie;
        private readonly MyComObject _myComObject;

        public MainWindow()
        {
            InitializeComponent();

            // register my object, the path is just to be able to get it from VBS
            _myComObject = new();
            RunningObjectTable.RegisterFile(_myComObject, @"x:\somepath");
        }

        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);
            // revoke the object
            if (_cookie != 0)
            {
                RunningObjectTable.Revoke(_cookie);
            }
        }
    }

    [ComVisible(true)]
    // VBScript only knows IDispatch
    [ClassInterface(ClassInterfaceType.AutoDispatch)]
    public class MyComObject
    {
        // add any public method here
        // note you are limited to COM automation types
        public string? ToUpperCase(string? text) => text?.ToUpper();
    }

    public static partial class RunningObjectTable
    {
        public static int RegisterFile(object instance, string filePath, ROTFLAGS flags = ROTFLAGS.ROTFLAGS_REGISTRATIONKEEPSALIVE)
        {
            Marshal.ThrowExceptionForHR(GetRunningObjectTable(0, out var table));
            Marshal.ThrowExceptionForHR(CreateFileMoniker(filePath, out var mk));
            Marshal.ThrowExceptionForHR(table.Register(flags, instance, mk, out var cookie));
            return cookie;
        }

        public static void Revoke(int cookie)
        {
            Marshal.ThrowExceptionForHR(GetRunningObjectTable(0, out var table));
            table.Revoke(cookie);
        }

        [DllImport("ole32")]
        private static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable pprot);

        [DllImport("ole32", CharSet = CharSet.Unicode)]
        private static extern int CreateFileMoniker(string lpszPathName, out IMoniker ppmk);

        [ComImport, Guid("00000010-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        private interface IRunningObjectTable
        {
            [PreserveSig]
            int Register(ROTFLAGS grfFlags, [MarshalAs(UnmanagedType.Interface)] object punkObject, IMoniker pmkObjectName, out int pdwRegister);
            [PreserveSig]
            int Revoke(int dwRegister);
            [PreserveSig]
            int IsRunning(IMoniker pmkObjectName);
            [PreserveSig]
            int GetObject(IMoniker pmkObjectName, [MarshalAs(UnmanagedType.Interface)] out object ppunkObject);
            [PreserveSig]
            int NoteChangeTime(int dwRegister, ref FILETIME pfiletime);
            [PreserveSig]
            int GetTimeOfLastChange(IMoniker pmkObjectName, out FILETIME pfiletime);
            [PreserveSig]
            int EnumRunning(out IEnumMoniker ppenumMoniker);
        }
    }

    [Flags]
    public enum ROTFLAGS
    {
        ROTFLAGS_NONE = 0,
        ROTFLAGS_REGISTRATIONKEEPSALIVE = 1,
        ROTFLAGS_ALLOWANYCLIENT = 2,
    }
}