与C#和VBA并排的COM Interop

我不是在谈论从C#调用VBA COM ……反过来说!

我想要做的是在MS Access中使用VBA调用C#库而无需注册DLL。 我一直在玩并排互操作一段时间没有成功,最后我发现mdb.manifest可能不是exe.manifest的可接受的替代品(可能很明显,我知道,但我我试图保持乐观。

我的问题:是否有可能让VBA加载并排的COM组件?

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

(在您提出问题之前,我的理由是:我绝对无法访问我的客户端的Windows注册表 – 这就是为什么它首先在Access中编写。而且,我需要在一个实现相同的function。 C#应用程序很快,而不是两次)。

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

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

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

编辑 :我刚刚尝试使用C:\windows\Microsoft.NET\Framework\v2.0.50727mscorlib.tlbmscoree.tlb – 加载在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"); } } } 

您无需选中“注册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存储在远程网络共享上,它将不起作用。 一个简单的解决方案是将其复制到使用它的每台PC上的同一本地文件夹中。 另一种解决方案是在您的Access app / VBA项目中包含二进制文件,并让MS-Access导出它们。 可以实现的一种方法是将它们存储在Base64中的表格或电子表格中,然后转换它们并将它们导出为二进制文件。

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

问题是要使用SxS,您需要拥有exe来设置配置以加载SxS程序集。 你没有“拥有”Access,虽然你可以放弃正确的配置以使其加载你的.NET COM东西没有注册,但它不会是一个“好公民”的举动。

如果你对填充变得棘手,你可以设置一个非托管DLL(或者一个带有dllexport的被攻击的C#类库,例如看到这个 ),导出将加载.NET框架,创建一个COMVisible DispInterface管理的实例输入并返回它(该方法应该返回IDispatch)。 然后写一个VBA声明到你的DLL导出函数(声明为返回Object)。 如果这没有意义,你可能不应该尝试… :)我之前在类似的情况下做过这个,它确实有效,但我没有一个样本指向你。

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

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