与 C# 和 VBA 的并行 COM 互操作

发布于 2024-08-15 15:22:20 字数 362 浏览 4 评论 0原文

我不是在谈论从 C# 调用 VBA COM...相反!

我想做的是在 MS Access 中使用 VBA 调用 C# 库,而不注册 DLL。我已经玩了一段时间的并行互操作,但没有成功,我终于意识到 mdb.manifest 可能不是 exe.manifest 的可接受的替代品(我知道,这可能是显而易见的,但我试图保持乐观)。

我的问题:是否可以让 VBA 加载并行 COM 组件?

或者,是否有另一种方法可以在 Access 中使用未注册的 C# 库?

(在你问之前,我的原因是:我绝对不可能被授予访问我客户的 Windows 注册表的权限——这就是为什么它首先是用 Access 编写的。而且,我需要在一个C# 应用程序很快,而不是重复)。

I'm not talking about calling a VBA COM from C#... the other way around!

What I would like to do is call a C# library using VBA in MS Access without registering the DLL. I've been playing around with side-by-side interop for a while without success and it has finally occurred to me that a mdb.manifest is probably not an acceptable replacement for an exe.manifest (probably obvious, I know, but I was trying to be optimistic).

My question: Is it possible to get VBA to load a side-by-side COM component?

Or, is there another way to use an unregistered C# library in Access?

(Before you ask, my reasons are: there is absolutely no way I will be granted access to my client's Windows registry -- that's why it was written in Access in the first place. And, I will need to implement the same functionality in a C# application soon and rather not do it twice).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

り繁华旳梦境 2024-08-22 15:22:20

添加到现有的答案:使用.NET 4.0,在 VBA 项目中使用 C# dll 实际上非常简单,而无需注册 COM。

编辑:我刚刚使用 C:\windows\Microsoft.NET 中的 mscorlib.tlbmscoree.tlb 尝试过此操作\Framework\v2.0.50727——加载在 3.5 中编译的程序集——并且工作得很好。显然你不需要 .NET 4.0。

下面是如何在 VBA 项目中使用 C# dll 的示例。它是从 稍作修改的这个答案。

1) 添加对 VBA 项目的以下类型库的引用(工具 -> 引用):(

C:\windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.tlb
C:\windows\Microsoft.NET\Framework\v4.0.30319\mscoree.tlb

如果运行 64 位 Office,则使用 Framework64 文件夹)

2) 在 C# 项目中,确保添加 [ComVisible (true)] 属性到您的类:

using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace VB6FuncLib
{
    [ComVisible(true)]
    public class VB6FuncLib
    {
        public VB6FuncLib()
        { }
        public void test()
        {
            MessageBox.Show("Test Successful");
        }
    }
}

不需要需要选中“Register for COM Interop”选项。这仅用于构建标准 COM 对象。您也不必选中“使程序集 COM 可见”,除非您希望整个程序集可见(这也将消除对 COMVisible 属性的需要)。

3) 在您的 VBA 代码中,使用以下代码添加一个新模块:

Sub Test()
    Dim Host As mscoree.CorRuntimeHost
    Set Host = New CorRuntimeHost
    Host.Start
    Dim Unk As IUnknown
    Host.GetDefaultDomain Unk
    Dim AppDomain As AppDomain
    Set AppDomain = Unk
    Dim ObjHandle As ObjectHandle
    Set FS = CreateObject("Scripting.FileSystemObject")
    Path = FS.GetParentFolderName(CurrentDb().Name)
    Set ObjHandle = AppDomain.CreateInstanceFrom(Path & "\VB6 Function Library.dll", "VB6FuncLib.VB6FuncLib")
    Dim ObjInstance As Object
    Set ObjInstance = ObjHandle.Unwrap
    ObjInstance.test
    Host.Stop
End Sub

4) 将 DLL 复制到与您的 Office 项目相同的文件夹中,并在 VBA 中运行 Test() 子项。

注意

应该注意的是,该技术的局限性之一是,如果 .DLL 存储在远程网络共享上,则该技术将不起作用。一种简单的解决方案是将其复制到每台使用它的电脑上的同一本地文件夹中。另一种解决方案是将二进制文件包含在 Access 应用程序/VBA 项目中,并让 MS-Access 导出它们。可以实现的一种方法是将它们以 Base64 形式存储在表或电子表格中,然后将它们转换并导出为二进制文件。

通过创建一个与 DLL 配合使用的类型库(通过使用 tlbexp),并在我的 VBA 项目中添加对 TLB 的引用,我能够让早期绑定(以及 Microsoft IntelliSense)发挥作用,但这确实使事情变得有点复杂因为它要求您的 VBA 应用程序知道 DLL 和 TLB 文件在哪里(并且还需要有人确保它们在那里)。

To add to the already existing answers: with .NET 4.0, it's actually quite simple to consume a C# dll in your VBA project without registering the COM.

EDIT: I just tried this with the mscorlib.tlb and mscoree.tlb that are in C:\windows\Microsoft.NET\Framework\v2.0.50727-- loading an assembly compiled in 3.5-- and it worked just fine. So apparently you don't need .NET 4.0.

The below is an example of how to use a C# dll in your VBA project. It is slightly modified from this answer.

1) Add references to the following type libs your VBA project (Tools->References):

C:\windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.tlb
C:\windows\Microsoft.NET\Framework\v4.0.30319\mscoree.tlb

(use Framework64 folder if you are running 64-bit Office)

2) In your C# project, make sure you add the [ComVisible(true)] attribute to your class:

using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace VB6FuncLib
{
    [ComVisible(true)]
    public class VB6FuncLib
    {
        public VB6FuncLib()
        { }
        public void test()
        {
            MessageBox.Show("Test Successful");
        }
    }
}

You don't need to check the option "Register for COM Interop". That's only for building a standard COM object. You don't have to check "Make Assembly COM Visible" either, unless you want the whole assembly to be visible (that would also eliminate the need for the COMVisible attribute).

3) In your VBA code, add a new module with this code:

Sub Test()
    Dim Host As mscoree.CorRuntimeHost
    Set Host = New CorRuntimeHost
    Host.Start
    Dim Unk As IUnknown
    Host.GetDefaultDomain Unk
    Dim AppDomain As AppDomain
    Set AppDomain = Unk
    Dim ObjHandle As ObjectHandle
    Set FS = CreateObject("Scripting.FileSystemObject")
    Path = FS.GetParentFolderName(CurrentDb().Name)
    Set ObjHandle = AppDomain.CreateInstanceFrom(Path & "\VB6 Function Library.dll", "VB6FuncLib.VB6FuncLib")
    Dim ObjInstance As Object
    Set ObjInstance = ObjHandle.Unwrap
    ObjInstance.test
    Host.Stop
End Sub

4) Copy the DLL into the same folder as your Office project and run the Test() sub in VBA.

Notes:

It should be noted that one of the limitations of this technique is that it won't work if the .DLL is stored on a remote network share. One simple solution would be to copy it into the same local folder on each PC where it is being used. Another solution would be to include the binaries in your Access app/VBA project, and have MS-Access export them. One way that could be accomplished would be by storing them in Base64 in a table or spreadsheet, then converting them and exporting them as binary.

I was able to get early binding (and therefore Microsoft IntelliSense) to work by creating a type library to go with the DLL (by using tlbexp), and adding a reference to the TLB in my VBA project, but it does complicate matters a bit because it requires your VBA app to know where both the DLL and the TLB files are (and also requires someone to make sure they are there).

知你几分 2024-08-22 15:22:20

您不必拥有该 exe 即可使用 SxS,SxS 是 激活上下文
如果您可以将相关的 win32 调用导入到 vba 中(并且您可以),那么您可以使用 激活上下文 api 来加载清单文件。

有关该主题的更多信息和一些示例可以在此处找到。

You don't have to own the exe to use SxS, SxS is another word for Activation Context.
If you can import the relevant win32 calls into vba (and you can), then you can use the activation context api to load your manifest file.

More on the subject and some examples can be found here.

姜生凉生 2024-08-22 15:22:20

问题是,要使用 SxS,您需要拥有 exe 来设置配置来加载 SxS 程序集。您并不“拥有”Access,虽然您可以放入正确的配置以使其无需注册即可加载您的 .NET COM 内容,但这并不是一个“好公民”的举动。

如果您对填充感到棘手,您可以设置一个非托管 DLL(或带有 dllexport 的黑客 C# 类库,请参阅 this,例如),带有将加载 .NET 框架的导出,创建 COMVisible DispInterface 托管类型的实例并返回它(该方法应返回 IDispatch)。然后向 DLL 导出函数写入 VBA 声明(声明为返回对象)。如果这没有意义,您可能不应该尝试...:)我之前在类似的情况下做过此操作,并且它确实有效,但我没有示例可以向您指出。

The problem is that to use SxS, you need to own the exe to set up the config to load the SxS assembly. You don't "own" Access, and while you could drop the right config in to cause it to load your .NET COM stuff sans registration, it wouldn't be a "good citizen" move.

If you get tricky with shimming, you can set up an unmanaged DLL (or a hacked C# class library with a dllexport, see this, for example) with an export that will load the .NET framework, create an instance of a COMVisible DispInterface managed type and return it (the method should return IDispatch). Then write a VBA declare to your DLL export function (declared as returning Object). If this doesn't make sense, you probably oughtn't try it... :) I've done this before in a similar situation, and it does work, but I don't have a sample to point you at.

半衾梦 2024-08-22 15:22:20

C# 库不是常规的 DLL。它们更类似于 COM 库,在使用之前需要注册(就像 ActiveX 控件一样);特别是当从非 .NET 代码调用时。

(当然,除非事情发生了变化……)

C# libraries are not regular DLLs. They're more similar to COM libraries which need to be registered (just like ActiveX controls) before being used; especially when called from non-.NET code.

(Unless, of course, things have changed...)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文