【发布时间】:2013-09-23 12:49:36
【问题描述】:
我正在尝试使用 c++ 类构建 ATL COM,并通过自动化将其添加到 excel 中。我找到了一些指南,但我有很多问题,一个是我的 dll 没有在自动化中进行比较,如果我尝试添加它,excel 表示存在关于不包含服务器或没有权限的问题。有人可以给我指导吗?我正在使用 Visual Studio 2012。谢谢。
【问题讨论】:
我正在尝试使用 c++ 类构建 ATL COM,并通过自动化将其添加到 excel 中。我找到了一些指南,但我有很多问题,一个是我的 dll 没有在自动化中进行比较,如果我尝试添加它,excel 表示存在关于不包含服务器或没有权限的问题。有人可以给我指导吗?我正在使用 Visual Studio 2012。谢谢。
【问题讨论】:
我将给出一个简单的示例,说明如何创建一个可在 Excel 的 vba 中引用的 c++ ATL/COM dll。该 dll 将向 VBA 公开一个 ATL/COM 对象,并且该对象将具有一个方法,该方法能够从 excel/vba 中获取一个 double,将其乘以 2.0 并将其输出到 VBA。 VBA 代码将可用于定义 excel 函数。当然,没有必要求助于 c++ dll 来让一个 excel 函数将单元格的内容乘以 2,但这只是为了演示。
开始前的最后一句话:最好以管理员身份运行 Visual Studio(在此答案中简称为 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 所指向的 double。现在,我们必须相应地修改 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 版本,并创建一个电子表格(xlsm 格式用于早期的 excel 版本和 xls对于后期版本,因为我们需要宏)称为 MyATLProjectTEST.xls。确保启用所有宏的使用。 (操作方法因 excel 的版本而异,但您可以通过朋友 google 找到操作方法。) Alt+F11 打开 VBA。单击工具、引用,浏览到您的 MyATLProject.dll 并单击以添加对它的引用。 (为了安全起见,可能是 regsvr32 之前的 dll --> 这在互联网上到处都有解释,谷歌再次成为你的朋友。)插入一个模块,它将自动称为 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 代码卖给某人,他放了一个在 B2 中使用字符串 LUDWIGVONMISES,而不是输入数字。会发生什么 ?一个错误:处于“非调试”模式,您必须在调试中跟踪以查看它发生的位置。为什么 ?因为你根本不处理错误。为了处理它们,您应该在 c++ 和 VBA 之间传递 VARIANT *,并在 c++ 中操作,例如在 MULT 方法的开头,检查传递给 MULT 的 VARIANT * 指针是否指向 VARIANT"包装“double 而不是string。
2)
制作 COM 自动化插件并不是添加用户定义的最佳方式 Excel 的功能 - 它很慢并且有各种限制。更好的是 制作等
我非常关注我正在处理和交付的代码的执行时间,并且已经为 excel/VBA 以及使用 excel sdk 的 xla/xll 开发了许多 ATL/COM dll,我愿意完全不同意 Govert 的这种说法。 对于上面的小演示,我们能说什么?当我们进入 MULT 方法时,我们已经进入了 c++,我们的计算(乘以 2)是在方法内部完成的。这很快,但想象一下,所完成的计算在数值或内存方面是非常密集的。我们可能很愚蠢,在我们的 ATL/COM 方法中这样做,但我们可以将计算驱逐到我们的方法之外,用纯 c++。这将是最快的事情,而且,这是唯一合理的方式。如果您想使用 excel sdk 直接生成 excel 函数,而不在 VBA 代码中包装 ATL/COM 方法来生成这些 excel 函数,则也是如此。在这里你可以说:嘿,使用 excel sdk 的 xll 选项会更好,因为我不会浪费包装时间。也许吧,但是包装时间真的没那么大,而且有了 ATL/COM,你就有了 VBA,你可以在其中以一种非常优雅的方式使用 ATL/COM 对象,你可以用它来生成 com 对象等。你甚至可以通过在 VBA 中包装 ATL/COM 方法编码的 excel 函数访问 excel 内存以实例化对象! 此外,任何此类 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 并不容易)。你可能想看看:
如果您更喜欢使用托管语言,例如 VB.NET、C# 或 F#,您应该使用开源 Excel-DNA 库,它允许使用 C API 将 .NET 与 Excel 集成,并且还具有各种高级功能。
【讨论】: