Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.net5 com library in access vba

We have an old Access Tool which needs to open guis written in C# - Winforms. At the moment we are simply starting the exe but if possible I would like to directly load the dll.

So far I have this code to create the com visible library

using System;
using System.Runtime.InteropServices;

namespace COMs {
    [ComVisible(true)]
    [Guid("C412E308-0D12-42D1-9506-C64A7958B4F9")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IProduktionsstundenCOM {
        public void StartProduktionsstunden();
    }

    [ComVisible(true)]
    [Guid("C63D94CD-4978-40C2-AD88-799D9430683F")]
    public class ProduktionsstundenCOM : IProduktionsstundenCOM {

        public void StartProduktionsstunden() {
            throw new NotImplementedException();
        }
    }
}

It builds and the .comhost.dll is created which I then register with regsvr32 appname.comhost.dll.

After doing so I cannot find the library in the Access references window and if I browse to it and try to add it I get this error "Reference to selected file cannot be added" ("Verweis auf die angegebene Datei kann nicht hinzugefügt werden."). The only helpful article I can find is this one https://learn.microsoft.com/en-us/dotnet/core/native-interop/expose-components-to-com which also states that .net5 can not create .tlb file which I don't know if I even need them.

Is there a way to make this work?

like image 665
DemosKadi Avatar asked Dec 06 '25 20:12

DemosKadi


1 Answers

I had a similar problem and came across the following github repository which shows how the IDL file should look like. It also shows you which additional registry entries you will need. You might use it as a starting point. link

The basic structure of your IDL file should look something like this:

[
    uuid(your library GUID),
    version(1.0),
    helpstring("ComTestLibrary")
]
library ComTestLibrary
{
    importlib("STDOLE2.TLB");

    [
        odl,
        uuid(your interface GUID),
        dual,
        oleautomation,
        nonextensible,
        helpstring("ComTestLibrary"),
        object,
    ]
    interface IComTest : IDispatch
    {
        [
            id(1),
            helpstring("Test")
        ]
        HRESULT Test(
            [in] double firstValue,
            [in] double secondValue,
            [in] BSTR comment,
            [out, retval] double* ReturnVal);

        // Other methods
    };

    [
        uuid(your class GUID),
        helpstring("ComTest")
    ]
    coclass ComTest
    {
        [default] interface IComTest;
    };
}

And for the registry entries you can add the DllRegisterServer and DllUnregisterServer functions to your COM-Class. In my case those look like this:

[ComRegisterFunction]
public static void DllRegisterServer(Type t)
{
      // Additional CLSID entries
      using (RegistryKey key = Registry.ClassesRoot.CreateSubKey(@"CLSID\{" + AssemblyInfo.ClassGuid + @"}"))
      {
           using (RegistryKey typeLib = key.CreateSubKey(@"TypeLib"))
           {
                typeLib.SetValue(string.Empty, "{" + AssemblyInfo.LibraryGuid + "}", RegistryValueKind.String);
           }
      }

      // Interface entries
      using (RegistryKey key = Registry.ClassesRoot.CreateSubKey(@"Interface\{" + AssemblyInfo.InterfaceGuid + @"}"))
      {
           using (RegistryKey typeLib = key.CreateSubKey(@"ProxyStubClsid32"))
           {
                typeLib.SetValue(string.Empty, "{00020424-0000-0000-C000-000000000046}", RegistryValueKind.String);
           }

           using (RegistryKey typeLib = key.CreateSubKey(@"TypeLib"))
           {
                typeLib.SetValue(string.Empty, "{" + AssemblyInfo.LibraryGuid + "}", RegistryValueKind.String);
                Version version = typeof(AssemblyInfo).Assembly.GetName().Version;
                typeLib.SetValue("Version", string.Format("{0}.{1}", version.Major, version.Minor), RegistryValueKind.String);
           }
      }

      // TypeLib entries
      using (RegistryKey key = Registry.ClassesRoot.CreateSubKey(@"TypeLib\{" + AssemblyInfo.LibraryGuid + @"}"))
      {
            Version version = typeof(AssemblyInfo).Assembly.GetName().Version;
            using (RegistryKey keyVersion = key.CreateSubKey(string.Format("{0}.{1}", version.Major, version.Minor)))
            {
                // typelib key for 32 bit
                keyVersion.SetValue(string.Empty, AssemblyInfo.Attribute<AssemblyDescriptionAttribute>().Description, RegistryValueKind.String);
                using (RegistryKey keyWin32 = keyVersion.CreateSubKey(@"0\win32"))
                {
                    keyWin32.SetValue(string.Empty, Path.ChangeExtension(Assembly.GetExecutingAssembly().Location, ".comhost.tlb"), RegistryValueKind.String);
                }

                // typelib key for 64 bit
                keyVersion.SetValue(string.Empty, AssemblyInfo.Attribute<AssemblyDescriptionAttribute>().Description, RegistryValueKind.String);
                using (RegistryKey keyWin64 = keyVersion.CreateSubKey(@"0\win64"))
                {
                    keyWin64.SetValue(string.Empty, Path.ChangeExtension(Assembly.GetExecutingAssembly().Location, ".comhost.tlb"), RegistryValueKind.String);
                }

                using (RegistryKey keyFlags = keyVersion.CreateSubKey(@"FLAGS"))
                {
                    keyFlags.SetValue(string.Empty, "0", RegistryValueKind.String);
                }
           }
      }
}

[ComUnregisterFunction]
public static void DllUnregisterServer(Type t)
{
        Registry.ClassesRoot.DeleteSubKeyTree(@"TypeLib\{" + AssemblyInfo.LibraryGuid + @"}", false);
        Registry.ClassesRoot.DeleteSubKeyTree(@"Interface\{" + AssemblyInfo.InterfaceGuid + @"}", false);
        Registry.ClassesRoot.DeleteSubKeyTree(@"CLSID\{" + AssemblyInfo.ClassGuid + @"}", false);
}
like image 85
tabaluga Avatar answered Dec 08 '25 10:12

tabaluga



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!