如何将COM对象从VBA传递到C ++ DLL

时间:2014-08-14 17:15:03

标签: c# c++ .net excel-vba com

我有一个Excel / VBA应用程序,它通过DLL将繁重的工作外包给C ++。我目前使用一长串参数传递两者之间的输入/输出,这些参数是基本数据类型和指针。我试图重构双方的代码,使其更加模块化,并意识到有一个我可以传递的公共对象是有帮助的,而不是必须从非结构化的指针列表中重新创建C ++方面的结构。以为我会尝试COM / .NET,这就是我所在的地方:

1)使用C#类库创建一个COM对象,沿着以下行http://www.codeproject.com/Articles/12673/Calling-Managed-NET-C-COM-Objects-from-Unmanaged-C

// My_class.cs
namespace Interop {
  public interface IMy_class { ... }
  public class My_class : IMy_class  { ... }
}

2)使用Interop.dll

生成Interop.tlb并注册regasm /codebase /tlb

3)在VBA中,添加对Interop.tlb的引用,试图将实例传递给C ++ DLL函数:

// VBA
Declare Sub my_func Lib "my_func.dll" (ByRef mc as Interop.My_class)

Sub test_interop()
  Dim mc as Interop.My_class
  Set mc = New Interop.My_class
  my_func mc
End Sub

// my_func.cpp (exports my_func to my_func.dll)
#import "Interop.tlb"

void __stdcall my_func(Interop::IMy_classPtr icp) { ... }

VBA似乎创建了实例ok但是对my_func的调用导致Excel崩溃。我可以在调试模式下看到从Excel收到的icp的值等于VarPtr(mc)而不是ObjPtr(mc)我认为是接口的地址。所以我尝试将其更改为my_func(Interop::My_class* mcp),我至少可以进入和退出该函数而不会崩溃,但似乎My_class没有可用的功能。该功能似乎包含在名为_My_class的内容中,因此我尝试了my_func(Interop::_My_class* mcp),但是当我尝试与mcp进行交互时,我就崩溃了。与my_func(Interop::_My_classPtr mcp)

相同的结果

使用原始函数,我还尝试使用来自VBA的ObjPtr(mc)DispCallFunc直接传递给'icp'。我再次进入和退出函数,但是当我尝试与对象交互时崩溃了。实际上我可以从中读取而不会崩溃(虽然不是正确的值)但是在我尝试写入时出现此错误:Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually the result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.

这里没有选项,想知道我是否接近,是否不可能,或者这是不是正确的做法。

注意我还直接在C ++中测试了C#类库。我可以使用CoCreateInstance创建一个实例并通过接口与它进行交互,所以如果从C ++端启动它似乎工作正常。

1 个答案:

答案 0 :(得分:1)

COM和Interop做了一些内部管道,让你在这里绊倒。我相信你正在寻求按照以下方式做的事情:

 dim myObj as Interop.IMyClass    # Note: Interface, not the class
 set myObj = new Interop.MyClass
 myObj.name = "My Name"
 myObj.age  = 69
 myObj.job  = "Chief Stamp Licker"

 my_func myObj

然后在my_func里面:

 std::cout << "Name: " << pMyObj->name() << ", "
           << "Age : " << pMyObj->age()  << ", "
           << "Job : " << pMyObj->job()  << std::endl;

..并打印出发送的信息。

为此,您需要增强界面以提供&#39;属性&#39;。您希望能够读取或写入的每个属性都需要get和set方法。这可能看起来很乏味,但是你有很好的理由想要这样做。

或者,您可以在界面上使用一些方法,这些方法可以一次性设置多个属性:

myObj.setProperties("My Name", 69, "Chief Stamp Licker")
my_func myObj

..您可以更轻松/更快地实施,但扩展灵活性更低,更耗时。介于两者之间的东西可能适合你 - 例如setPerson和setOccupation,它接受多个参数。

你不能直接写入myObj变量的原因是&#39; myObj&#39;在Visual Basic运行时中,不是对象,它是由Visual Basic运行时环境,COM子系统,DCOM子系统,C#互操作层或其组合构成的代理对象的接口,取决于您的COM对象注册,线程模型以及Excel如何初始化(您无法控制)。