2009-12-14 36 views
6

我不是在说从C#调用VBA COM ......反过来!与C#和VBA并行COM对话

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

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

或者,有没有另一种方式来使用Access中的未注册的C#库? (在你问之前,我的理由是:我绝对没有办法让我访问我的客户端的Windows注册表 - 这就是为什么它首先被写入Access。而且,我需要实现相同的功能在C#应用程序很快,而不是做两次)。

回答

3

您不必拥有使用SxS的exe文件,SxS是另一个词Activation Context。 如果您可以将相关的win32调用导入vba(并且您可以),那么您可以使用activation context api加载清单文件。

更多关于这个问题和一些例子可以找到here

+0

我看着使用该API。但具有讽刺意味的是,微软创建了一个包来避免需要注册的注册,并留下了我之前的相同问题 - 如何在我的客户机上获取?尽管Microsoft.Windows.Actctx可用于Windows 2K3 +,但它仅包含在服务器安装中。还有另一个SO问题在这里抱怨:http://stackoverflow.com/questions/979567/microsoft-windows-actctx-on-windows-xp。 MSDN支持它:http://msdn.microsoft.com/en-us/library/aa375644(VS.85).aspx。 – Jelly 2010-01-15 14:15:39

+0

从我所知道的Activation Context API是Kernel32的一部分。lib,这意味着你不必安装任何东西,并且它包含在任何支持WinXP SP2和更高版本的SxS的操作系统中。 我在WinXP SP3上完成了自己的工作,并且没有特别的安装。 我不知道Microsoft.Windows.Actctx,但我100%确定你可以通过从Kernel32.lib导入相关函数直接调用API来直接调用API。就像这个示例代码一样http://www.mazecomputer.com/sxs/help/sxsapi3.htm – 2010-01-16 20:35:50

+0

哦,我明白了。我在网上看到的所有在VBA中使用Actctx对象的例子都是使用Actctx对象,而不是API,这是我最初得出结论认为它不能用代码完成的唯一原因。显然,它是两种方式(并称为同一件事)只是为了让我困惑。我应该不会有太多的麻烦让它启动并运行 - 它应该是一个比黑客攻击更稳定的解决方案 - 所以当我到达那里时我会移动接受的解决方案标志。谢谢您的帮助! – Jelly 2010-01-19 16:52:41

0

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

(除非,当然,一切都变了......)

+0

我的印象是,可以使用未注册的C#DLL来自:http://msdn.microsoft.com/en-us/library/ms973915.aspx其中有一个C#.NET服务器和一个非.NET客户端。除了客户端是VB6可执行文件而不是VBA应用程序之外,我相信这基本上与我想要做的相同。还有什么我失踪的? 谢谢! – Jelly 2009-12-14 21:07:00

+0

您可能会遇到需要为客户端创建清单文件的部分,因为VBA不是您可以生成清单文件的独立程序。清单看起来很简单,但我无法确定如何正确地将其关联。也许你可以让VBA执行一个独立的客户端来满足你的需求? – Ioan 2009-12-15 13:30:58

1

的问题是,使用的SxS,你需要自己的exe来设置配置加载的SxS装配。你不需要“拥有”Access,并且尽管你可以放弃正确的配置来使它加载你的.NET COM的sans注册,但它不会是一个“良好的公民”举措。

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

+0

这非常聪明,它确实有道理。在我搜索关于黑客攻击IL以强制C#导出方法之前,我没有发现任何东西。我得到了这个部分的工作,我认为它不应该太难以让它成为返回对象的入口点。感谢您的建议! – Jelly 2009-12-17 16:56:41

8

要添加到已经存在的答案: .NET 4.0 ,它实际上是很简单的消耗你的VBA项目中的C#DLL没有注册COM。

编辑:我刚刚与mscorlib.tlbmscoree.tlb是在C:\windows\Microsoft.NET\Framework\v2.0.50727尝试这一点 - 在加载编译3.5--组件和它的工作就好了。所以显然你不需要.NET 4.0。

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

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

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

(使用Framework64文件夹,如果你正在运行64位Office)

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"); 
     } 
    } 
} 

需要CHEC k选项“注册COM Interop”。这仅用于构建标准的COM对象。您不必检查“Make Assembly COM Visible”,除非您希望整个组件可见(这也将消除对属性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()子。

注:

应当指出的是,这种技术的局限性之一是,如果.DLL被存储在远程网络共享将无法正常工作。一个简单的解决方案是将其复制到每个使用该PC的本地文件夹中。另一种解决方案是将二进制文件包含在Access应用程序/ VBA项目中,并让MS-Access导出它们。一种可以实现的方法是将它们存储在Base64中的表格或电子表格中,然后转换它们并将它们导出为二进制文件。

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