简体   繁体   中英

How to use a C# DLL in Microsoft Access

I have created the class below in Visual Studio 2019 with "Make Assembly COM-Visible" set to true and "Register for COM interop" checked.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace SimpleCalc
{

    [InterfaceType(ComInterfaceType.InterfaceIsDual)]
    [Guid("95F54E1A-826C-4766-9FE9-D32C47B07504")]
    public interface _Test
    {
        string HelloWorld { get; }
    }

    [ClassInterface(ClassInterfaceType.None)]
    [Guid("9F7248AF-4E59-4938-AECC-B938703F33BB")]
    [ProgId("SimpleCalc.Test")]
    public class Test : _Test
    {
        public string HelloWorld { get { return "Hello, World! "; } }
    }
}

Code compiles fine and I can reference it in VBA and the property "Hello World" is shown in the class "Test" in the object browser.

However when I try to access in an onClick event subroutine with the code

Dim o As Test
Set o = New Test 
MsgBox o.HelloWorld

error:'429' "ActiveX component can't create object" is generated at Set o = New Test.

What am I doing wrong?

Well, first of all? You not marked the interface as public

eg;

    public interface _Test

Next up, I would remove the Prog_id - you don't need it, and I think that also messing this up.

And, really, I don't see any case to be made for building a infterface here? I doubt in c# (or for sure in VBA), you not going to need to declarer public member variable types derived from that class without having to create a instance of that class (it you need to do that - then ok - I suppose one can make the case for a interface, but OTHER then this reason? No need).

One does not really need to bother with the GUIDS in the code either. The project settings will take care of that.

And you don't really need a namespace - again project default takes care of that.

So, now we down to this:

using System.Runtime.InteropServices;

[ClassInterface(ClassInterfaceType.AutoDual)]
public class COMTest1
    {
        public string HelloWorld
        {
            get
            {
                return "Hello, World! ";
            }
        }

    }

Ok, now that is much better, Of course: we need to check the "com visible" box here:

在此处输入图像描述

or you can just above the the class interface type setting add a COMvisible=true.

And during development, you can (should) have the COM assembly registered. On target machines you have to do a regasm.exe, but on your dev box, you can get VS to do this for you. Eg here:

在此处输入图像描述

Now above does ZERO to the project - it ONLY does that regasm for you.

At this point, then in VBA, you can use late binding, or early. Assuming we set a refernce in tools->reference from VBA, like this:

在此处输入图像描述

And now our VBA code will look like this:

Sub Test23434324()

  Dim cTest As New CShapeCOMTest.ComTest1
  
  Debug.Print c.HelloWorld
  
End Sub

So, I would not bother with a interface - but yours was not public.

The above is all you really need for this to work. And in place of a interace, simply mark members as public or private. And this includes you dumping getters/setters.

You can just at the class level have this:

public class COMTest1
    {

public string CompanyName;
public string PhoneNumber;

        public string HelloWorld
        {
            get
            {
                return "Hello, World! ";
            }
        }

    }

And those pubic members will show up in VBA intel-sense.

So, I don't even bother with a interface.

I often don't bother with get/sets for simple public members (say a string)

As a general rule? Build a simple clean class - you only add the interface tag, no GUIDS, no nothing else and no need for the interface either. Most of my classes are written this way. So not much love and care required here.

In fact, you don't even need the Classinterface tag - you can just set the make com assembly true check box in the above assembly window. (but, you lose intei-sense on the VBA com side if you do this ).

The less code, the more we pair this down to the absolute min?

The less code to write, and the less problems to mess this up.

***Edit: ************************************

The working tested example is this:

using System.Runtime.InteropServices;

[ClassInterface(ClassInterfaceType.AutoDual)]
public class COMTest1
    {

public string CompanyName;
public string PhoneNumber;

        public string HelloWorld
        {
            get
            {
                return "Hello, World! ";
            }
        }

    }

Force the project to x86. This will force regams.exe to correct register. If you been running Access + testing - you have to exit access else the.dll gets locked when it runs that code.

Remember to set under assembly MAKE assembly COM visible.

Remember to check register for com interop (build tab on project).

With above, in VBA, you even see intel-sense like this:

在此处输入图像描述

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM