【问题标题】:Using C++ DLL in Excel VBA在 Excel VBA 中使用 C++ DLL
【发布时间】:2016-06-10 15:53:02
【问题描述】:

我从https://msdn.microsoft.com/de-de/library/ms235636.aspx 创建MathFuncsDll.dll 没有问题。 我也可以毫无问题地在不同的 C++ 项目中使用数学函数。

现在我想在 Excel VBA 中使用这些函数。我试过了:

Public Declare Function DLL_Import_Add Lib "C:\Users\User\Desktop\MathFuncsDll.dll" Alias "Add" (ByVal a As Double, ByVal b As Double) As Double

Sub test()
    MsgBox DLL_Import_Add(2.1, 3.3)
End Sub

但是我收到一个Run-time error '453' DLL 入口点找不到。

我该如何解决这个问题?

MathFuncsDll.h:

// MathFuncsDll.h
#ifdef MATHFUNCSDLL_EXPORTS
#define MATHFUNCSDLL_API __declspec(dllexport)
#else
#define MATHFUNCSDLL_API __declspec(dllimport)
#endif

namespace MathFuncs
{
    // This class is exported from the MathFuncsDll.dll
    class MyMathFuncs
    {
    public:
        // Returns a + b
        static MATHFUNCSDLL_API double Add(double a, double b);
        // Returns a - b
        static MATHFUNCSDLL_API double Subtract(double a, double b);
        // Returns a * b
        static MATHFUNCSDLL_API double Multiply(double a, double b);
        // Returns a / b
        // Throws const std::invalid_argument& if b is 0
        static MATHFUNCSDLL_API double Divide(double a, double b);
    };
}

MathFuncsDll.cpp:

// MathFuncsDll.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
#include "MathFuncsDll.h"
#include <stdexcept>
using namespace std;
namespace MathFuncs
{
    double MyMathFuncs::Add(double a, double b)
    {
        return a + b;
    }
    double MyMathFuncs::Subtract(double a, double b)
    {
        return a - b;
    }
    double MyMathFuncs::Multiply(double a, double b)
    {
        return a * b;
    }
    double MyMathFuncs::Divide(double a, double b)
    {
        if (b == 0)
        {
            throw invalid_argument("b cannot be zero!");
        }
        return a / b;
    }
}

【问题讨论】:

    标签: c++ vba excel


    【解决方案1】:

    我假设你知道自己在做什么,当从 VB 程序调用 C++ 库时,我指的是calling convention


    问题是 C++ 名称是 mangled

    如果您查看导出的函数名称(例如 dumpbin /EXPORTS MathFuncsDll.dll,它们是这样的:

    ordinal hint RVA      name
    
          1    0 00001000 ?Add@MyMathFuncs@MathFuncs@@SAHHH@Z
          2    1 00001030 ?Divide@MyMathFuncs@MathFuncs@@SAHHH@Z
          3    2 00001020 ?Multiply@MyMathFuncs@MathFuncs@@SAHHH@Z
          4    3 00001010 ?Subtract@MyMathFuncs@MathFuncs@@SAHHH@Z
    

    我的输出与你的不同,但足以表明没有像Add 这样的简单名称。

    通常使用修饰符 extern "C" 可以避免这种混乱。
    使用 Microsoft 编译器,类成员名称似乎总是被弄乱,所以在这里没用。

    您可以通过四种方式解决此问题:

    1.定义重命名函数的DEF 文件

    LIBRARY MathFuncsDll
    EXPORTS
       Add=?Add@MyMathFuncs@MathFuncs@@SAHHH@Z
       ...
    

    2。在 VBA 声明中使用正确的别名

    Public Declare Function DLL_Import_Add Lib "C:\Users\User\Desktop\MathFuncsDll.dll" Alias "?Add@MyMathFuncs@MathFuncs@@SAHHH@Z" (ByVal a As Double, ByVal b As Double) As Double
    

    3.按序号导入,使用类似Alias "#1"

    Public Declare Function DLL_Import_Add Lib "C:\Users\User\Desktop\MathFuncsDll.dll" Alias "#1" (ByVal a As Double, ByVal b As Double) As Double
    

    4.定义调用其等价物的全局存根函数

    extern "C" MATHFUNCSDLL_API double Add(double a, double b)
    {
       return MathFuncs::MyMathFuncs::Add(a, b); 
    } 
    

    并导出它们。

    这些都不是完美的,请记住,错位名称是编译器特定的,如果您使用它们并更改/更新编译器,事情可能会破裂。

    序数也不是固定的,除非你使用 DEF 文件(同样需要重命名的名称)。

    最后一个选项对更改调用约定很有用,并且不会更改原始函数的导出方式,从而允许访问 C++ 和非 C++ 程序。

    【讨论】:

    • 就是这样!我试过Add@MyMathFuncs@MathFuncs@@SANNN@Z,但一开始我忘记了?。我也在 C 中做到了这一点,并在一个带有 DEF 文件的 DLL 中定义了函数的名称,并以相反的方式编写:name_in_dll = name_in_c。应该在你的答案中翻转吗?
    【解决方案2】:
    extern "C"
    {
    // This class is exported from the MathFuncsDll.dll
        class MyMathFuncs
        {
        public:
            // Returns a + b
            MATHFUNCSDLL_API double __stdcall Add(double a, double b);
            // Returns a - b
            MATHFUNCSDLL_API double __stdcall Subtract(double a, double b);
            // Returns a * b
            MATHFUNCSDLL_API double __stdcall Multiply(double a, double b);
            // Returns a / b
            // Throws const std::invalid_argument& if b is 0
            MATHFUNCSDLL_API double __stdcall Divide(double a, double b);
        };
    }
    

    从 vba 调用函数需要 __stdcall 调用约定 - 默认情况下,该函数将使用 __cdecl 调用约定进行编译。我还将返回类型切换为LONG,然后将指针传递给目标变量。您可以使用返回值来验证函数(如果它抛出错误或正常)...有点像HRESULT

    What is __stdcall? - 最佳答案很好地解释了不同的 c/c++ 调用约定。

    【讨论】:

      猜你喜欢
      • 2011-09-14
      • 1970-01-01
      • 2014-08-25
      • 2021-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多