简体   繁体   English

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

[英]How to pass a COM object from VBA to C++ DLL

I have an Excel/VBA application which outsources the heavy lifting to C++ via a DLL.我有一个 Excel/VBA 应用程序,它通过 DLL 将繁重的工作外包给 C++。 I currently pass the inputs/outputs between the two using a long list of arguments which are fundamental data types and pointers thereof.我目前使用一长串参数在两者之间传递输入/输出,这些参数是基本数据类型及其指针。 I'm trying to refactor the code on both sides to make it more modular and realize it would be helpful to have a common object I can pass across, rather than having to recreate the structure on the C++ side from an unstructured list of pointers.我正在尝试重构双方的代码以使其更加模块化,并意识到拥有一个可以传递的公共对象会很有帮助,而不必从非结构化的指针列表中重新创建 C++ 端的结构。 Thought I'd give COM/.NET a try and this is where I'm at:以为我会尝试一下 COM/.NET,这就是我所处的位置:

1) Created a COM Object using a C# Class Library along the following lines http://www.codeproject.com/Articles/12673/Calling-Managed-NET-C-COM-Objects-from-Unmanaged-C 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) Generated Interop.dll and registered Interop.tlb using regasm /codebase /tlb 2) 使用regasm /codebase /tlb生成Interop.dll并注册Interop.tlb

3) In VBA, add a Reference to the Interop.tlb , stuck on trying to pass an instance to C++ DLL function: 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 seems to create the instance ok but the call to my_func causes Excel to crash. VBA 似乎可以正常创建实例,但对 my_func 的调用导致 Excel 崩溃。 I can see in debug mode that the value of icp received from Excel equals VarPtr(mc) and not ObjPtr(mc) which I believe is the address of the interface.我可以在调试模式下看到从 Excel 收到的icp值等于VarPtr(mc)而不是ObjPtr(mc) ,我认为这是接口的地址。 So I tried changing it to my_func(Interop::My_class* mcp) and I can at least get in and out of the function without crashing, but it appears there's no functionality available to My_class.所以我尝试将其更改为my_func(Interop::My_class* mcp)并且我至少可以在不崩溃的情况下进出该函数,但似乎没有可用于 My_class 的功能。 The functionality seems to be wrapped in something called _My_class , so I tried my_func(Interop::_My_class* mcp) but then I crash as soon as I try to interact with mcp .该功能似乎包含在名为_My_class东西中,所以我尝试了my_func(Interop::_My_class* mcp)但是当我尝试与mcp交互时我就崩溃了。 Same result with my_func(Interop::_My_classPtr mcp)my_func(Interop::_My_classPtr mcp)相同的结果

With the original function, I also tried passing ObjPtr(mc) directly to 'icp' using DispCallFunc from VBA.使用原始函数,我还尝试使用 VBA 中的DispCallFuncObjPtr(mc)直接传递给“icp”。 Again I could get in and out of the function ok but crashed when I try to interact with the object.我再次可以正常进出该函数,但是当我尝试与对象交互时崩溃了。 Actually I could read from it without crashing (although not the correct values) but got this error when I tried to write to it: 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.实际上,我可以在不崩溃的情况下读取它(尽管不是正确的值),但是当我尝试写入它时出现此错误: 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. 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.

Running out of options here and wondering if I'm even close, whether it's impossible, or whether this is just not the right way to do it.在这里没有选择,想知道我是否接近,是否不可能,或者这是否不是正确的方法。

Note I also tested the C# Class Library directly in C++.注意我还直接在 C++ 中测试了 C# 类库。 I can create an instance using CoCreateInstance and interact with it via the interface, so it seems to work ok if initiated from the C++ side.我可以使用CoCreateInstance创建一个实例并通过接口与它交互,所以如果从 C++ 端启动它似乎可以正常工作。

COM and Interop does some internal plumbing which is tripping you up here. COM 和 Interop 做了一些内部管道,这让你在这里绊倒。 I believe you're looking to do something along the lines of:我相信你正在寻求做一些事情:

 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

and then inside my_func:然后在 my_func 中:

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

..and have it print out the information send over. ..并让它打印出发送过来的信息。

To do this, you need to enhance your interface to provide 'properties'.为此,您需要增强界面以提供“属性”。 You need a get and set method for each property you wish to be able to read or write.对于您希望能够读取或写入的每个属性,您都需要一个 get 和 set 方法。 This may seem tedious, but there are very good reasons why you want to do this.这可能看起来很乏味,但是您有充分的理由想要这样做。

Alternatively, you can have handful of methods on your interface which can set a number of properties in one go:或者,您可以在界面上使用一些方法,这些方法可以一次性设置许多属性:

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

..which would be easier/quicker for you to implement, but much less flexible and more time-consuming to extend. ..这对您来说实施起来更容易/更快,但扩展起来更不灵活且更耗时。 Something in-between may suit you - eg setPerson and setOccupation which take more than one parameter.介于两者之间的东西可能适合您 - 例如 setPerson 和 setOccupation 需要多个参数。

The reason you cannot just write directly into myObj's variables is that the 'myObj' in the Visual Basic runtime is not the object, it is an interface to a proxy object cooked up by either the Visual Basic runtime environment, the COM subsystem, DCOM subsystem, C# interop layer or some combination thereof, depending on your COM object registration, threading model and how Excel got initialised (which you cannot control).您不能直接写入 myObj 的变量的原因是 Visual Basic 运行时中的“myObj”不是对象,它是由 Visual Basic 运行时环境、COM 子系统、DCOM 子系统组成的代理对象的接口、C# 互操作层或其某种组合,具体取决于您的 COM 对象注册、线程模型以及 Excel 的初始化方式(您无法控制)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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