我正在尝试使用 c++ 类构建 ATL COM,并通过自动化将其添加到 excel 中。我找到了一些指南,但我有很多问题,一个是我的 dll 没有在自动化中进行比较,如果我尝试添加它,excel 表示存在关于不包含服务器或没有权限的问题。有人可以给我指导吗?我正在使用 Visual Studio 2012。谢谢。
2 回答
我将给出一个简单的示例,说明如何创建一个可在 Excel 的 vba 中引用的 c++ ATL/COM dll。dll 将向 VBA 公开一个 ATL/COM 对象,并且该对象将具有一个方法,该方法能够从 excel/vba 中获取一个双精度值,将其乘以 2.0 并将其输出到 VBA。VBA 代码可用于定义 Excel 函数。当然,没有必要求助于 c++ dll 来拥有一个将单元格内容乘以 2 的 excel 函数,但这只是为了演示。
开始前的最后一句话:更好地运行视觉工作室(在这个答案中简称为 mvs)作为所有这些的管理员。从 2005 年开始,所有版本的 mvs 都包含了这方面的工作。
让我们开始吧。打开你喜欢的 mvs,创建一个新项目:在 Visual c++ 模板项目类型列表中选择模板“ATL 项目”。将其命名为“MyATLProject”,并选择将其保存在“C:\Users...\Desktop”中。单击确定。这将打开一个新窗口。不要在完成时单击那里,而是在下一步:现在检查是否仅检查了“dll”,如果是,请单击完成。(当然,对于我们的基本需求,我们在这里不需要更多。
我们所做的只是创建了一个解决方案(在“C:\Users...\Desktop\MyATLProject”中,我将此路径称为 $(SOL)。)其中包含两个项目:MyATLProject 和 MyATLProjectPS。PS 结束的项目(PS 代表代理/存根)对于我们想要做的事情毫无用处,但无论如何。我们将在这里进行调试。做一个重建解决方案。(您会注意到 MyATLProjectPS 已从 rebuid 中跳过:这是正常的。)现在您可以看到一个文件夹 $(SOL)\MyATLProject\debug 已创建,其中包含各种文件,我们感兴趣的一个是MyATLProject.dll --> 这个文件是我们必须在 VBA 中引用的文件,并且我们公开了对象和方法。目前,它不会在 VBA 中向我们公开任何内容,因为我们还没有实现任何 ATL/COM 对象,更何况也没有任何方法。
现在,让我们创建一个 ATL/COM 对象。这有硬方式和软方式,我只会展示软方式。右键单击解决方案资源管理器中的“MyATLProject”项目,然后“添加”,然后“类”,在类别中选择“ATL”,然后选择“ATL 简单对象”。然后点击“添加”。在简单的名称中输入“MyATLObject”并单击完成。(这里我们可以做更多,但对于这样的介绍,我们不需要更多。)这将创建并打开一个“ MyATLObject.h ”文件。MyATLObject.cpp也已创建,MyATLProject.idl(在项目生成时出现)已被修改。这三个文件是我们打算做的神圣的三位一体。为了一切而重建。;-) 已创建 ATL/COM 对象 MyATLObject,目前没有任何方法。如果您将在那里引用 dll,您可以在 VBA 中看到它,但请耐心等待,我们稍后再做。
现在,让我们给这个对象一个方法。这有一种硬方法和一种软方法,我将向您展示两者,首先从硬方法开始。第一次修改。进入 MyATLProject.idl 并替换
interface IMyATLObject : IDispatch{
};
经过
interface IMyATLObject : IDispatch{
[id(1), helpstring("method MULT")] HRESULT MULT([in,out] DOUBLE* theDouble);
};
我们做了什么 ?idl 文件“引用我们的MyATLObject
”(我自愿含糊不清)(以及其他对象/接口,如果需要),我们在其中指定我们MyATLObject
将有一个名为 的MULT
方法,该方法将引用(指向)双精度. 正如您可能猜到的,MULT
将乘以 2.0 所指向的双精度。现在,我们必须相应地修改 MyATLObject.h 和 MyATLObject.cpp。第二次修改:转到 MyATLObject.h 并替换
public:
};
OBJECT_ENTRY_AUTO(__uuidof(MyATLObject), CMyATLObject)
最后由
public:
STDMETHOD(MULT)(DOUBLE* theDouble);
};
OBJECT_ENTRY_AUTO(__uuidof(MyATLObject), CMyATLObject)
我们做了什么 ?我们已经MULT
在类中声明了该方法,就像我们在“vanilla”c++ 中为类所做的那样。第三次也是最后一次修改:转到 MyATLObject.cpp 和之后
// CMyATLObject
添加
STDMETHODIMP CMyATLObject::MULT(DOUBLE* theDouble)
{
return S_OK;
}
S_OK
是类型HRESULT
,并通过 return 告诉我们MULT
方法是否完成得好。(对于我们在这里的计划,这里不需要更冗长。)现在我们必须MULT
真正实现该方法,例如像这样(我自愿在这里变得丑陋和不安全,稍后您将自己制作这些东西):
STDMETHODIMP CMyATLObject::MULT(DOUBLE* theDouble)
{
*theDouble *= 2.0 ;
return S_OK;
}
重建解决方案。ATL/COM 对象MyATLObject
现在已更新为现在有一个方法MULT
。这是为了(实际上不是很)添加方法的困难方法。软方法是使用向导:撤消我们所做的所有三个修改并重新构建,这样我们现在处于与添加 MULT 方法时相同的阶段。进入类视图,展开MyATLProject,右键单击界面IMyATLObject
(带有手柄图标的那个,图标看起来像一个小钥匙),单击“添加”,然后单击“添加方法”。这将打开添加方法向导。MULT
输入方法名,DOUBLE *
参数类型中选择,参数名中选择“theDouble” 。我们的参数是一个指针的事实DOUBLE *
将使我们能够访问 out 和 retval 参数属性。点击 ”in
out
“。然后单击添加,然后单击完成。这将重新创建我们之前手动完成的所有操作(MULT
仅用于,当然没有实现)。添加
*theDouble *= 2.0 ;
在 MyATLObject.cpp 文件中执行 MULT 方法的行。
警告:根据 mvs 版本,根据经验,可能会出现除 idl 文件部分之外的所有内容都重新创建良好的情况,请检查它,如果是这种情况,请按照我们最初所做的那样手动进行,在艰辛的道路。
现在,是时候在 Excel 的 VBA 中使用我们的 ATL/COM c++ dll 了。选择您最喜欢的 excel 版本,并创建一个名为 MyATLProjectTEST.xls 的电子表格(早期版本的 Excel 版本为 xlsm 格式,后期版本为 xls 格式,因为我们需要宏)。确保启用所有宏的使用。(操作方法因 excel 的版本而异,但您可以通过朋友 google 找到操作方法。) Alt+F11 打开 VBA。单击工具、引用,浏览到您的 MyATLProject.dll 并单击以添加对它的引用。(为了安全起见,可能是 regsvr32 之前的 dll --> 这在互联网上到处都有解释,google 又是你的朋友。)插入一个模块,它将自动称为 Module1。在其中,代码:
Public Function MULT(x As Double) As Double
Dim o As MyATLProjectLib.MyATLObject
Set o = New MyATLProjectLib.MyATLObject
Call o.MULT(x)
MULT = x
End Function
现在进入一张电子表格,3.14159
输入单元格 B2 假设,然后输入公式
=MULT(B2)
在单元格 B3 中,并享受结果。
备注:尝试在电子表格打开时在 mvs 中构建解决方案:您将有一个
Error 1 Could not delete file 'c:\users\...\desktop\myatlproject\myatlproject\debug\MyATLProject.dll'.
确保文件没有被其他进程打开并且没有写保护。我的ATL项目
错误。这是因为 dll 在 VBA 中被引用(使用),谁以某种方式“拥有”它,并要求 mvs 修改(重新编译)它。在每次修改代码时,您都必须关闭电子表格以进行构建/重建。演示结束。
现在,“有趣”的部分,调试会话。重建解决方案,重新打开电子表格并在需要时重新引用其中的 dll。在行上放一个断点
*theDouble *= 2.0 ;
在 mvs 中执行 ctrl+alt+p(或“调试”然后“附加到进程”),在列表中选择 excel 电子表格并稍等片刻以再次访问您的电子表格(实际上是让符号加载) . 现在打开 VBA 并在该行放置一个断点
Call o.MULT(x)
现在转到单元格 B3,并重新计算它。您将在 VBA 的指定行中断,如果您进入 (F8),您将在
*theDouble *= 2.0 ;
MyATLObject.cpp 中的行。
最后的一些评论。
1)DOUBLE *
即使它有效也不是将信息从 vba 传递到 c++ 以及从 c++ 传递到 vba 的正确方法,原因如下:您将 dll 和 vba 代码卖给某人,他将字符串 LUDWIGVONMISES 放在 B2 中而不是放一个数字。会发生什么 ?一个错误:处于“非调试”模式,您必须在调试中跟踪以查看它发生的位置。为什么 ?因为你根本不处理错误。为了处理它们,您应该VARIANT *
在 c++ 和 VBA 之间传递,并在 c++ 中操作,例如在 MULT 方法的开头,以检查VARIANT *
传递给的指针是否MULT
指向VARIANT
“包装” adouble
而不是 a string
。
2)
制作 COM 自动化加载项并不是将用户定义的函数添加到 Excel 的最佳方式 - 它速度慢并且有各种限制。更好的是制作等
我非常关注我正在处理和交付的代码的执行时间,并且已经为 excel/VBA 以及使用 excel sdk 的 xla/xll 开发了很多 ATL/COM dll,我完全不同意这一点戈弗特的声明。关于上面的小演示,我们能说什么?当我们进入 MULT 方法时,我们已经进入了 c++,我们的计算(乘以 2)是在方法内部完成的。这很快,但想象一下,所完成的计算在数值或内存方面是非常密集的。我们可能很愚蠢,在我们的 ATL/COM 方法中这样做,但我们可以将计算驱逐到我们的方法之外,用纯 c++。这将是最快的事情,而且,这是唯一合理的方式。如果您想使用 excel sdk 直接生成 excel 函数,无需在 VBA 代码中包装 ATL/COM 方法来生成这些 excel 函数,也是如此。在这里你可以说:嘿,使用 excel sdk 的 xll 选项会更好,因为我不会浪费包装时间。也许吧,但包装时间真的没那么大,此外,任何此类 ATL/COM dll 在 c# 项目中都是可引用的,并且可以像在 VBA 中使用它一样在那里使用。即使在java中。;-)这不是使用 excel 的 sdk 编码的任何 xla/xll 的情况,就像 Govert 宣传的解决方案一样。严重缺乏可重用性问题。
3) OP 想要使用 ATL COM 来做一些完全可以用它来做的事情,Govert 告诉他 ATL/COM 太慢了——这是错误的——并使用其他东西。为此,我应该对 Govert 的回答投反对票。;-)
制作 COM 自动化加载项并不是将用户定义的函数添加到 Excel 的最佳方式 - 它速度慢并且有各种限制。更好的是基于本机 C API ( http://msdn.microsoft.com/en-us/library/office/bb687883.aspx ) 制作一个 .xll 插件。如果您使用的是 C++,那么有许多工具包可以提供很大帮助(仅使用 SDK 并不容易)。你可能想看看:
- XLW - 标准起点的 API 的开源包装器
- Keith Lewis 的 xll 库- 使用现代 C++ 范例,并带有各种各样的示例项目。
- XLL+ - 一个备受推崇的商业工具包,具有异步功能和功能区集成等各种高级功能。
如果您更喜欢使用托管语言,如 VB.NET、C# 或 F#,则应使用开源Excel-DNA库,该库允许使用 C API 将 .NET 与 Excel 集成,并且还具有各种高级功能.