2

我无法让我的 C# Dll 与我的 Excel 宏启用电子表格一起调用 Dll 中的函数。如果它没有参数,我可以调用该函数,能够返回预期值。但是当我向函数添加输入参数时,我无法让 VBA 的 Dll 调用成功。

我的 C# 模块的总和如下:

using System;
using System.Runtime.InteropServices;
using RGiesecke.DllExport;

namespace RSExternalInterface
{
    public class RSExternalInterface
    {
        [DllExport("add", CallingConvention = CallingConvention.Cdecl)]
        public static int TestExport(int left, int right)
        {
            return left + right;
        }
    }
}

我的 VBA 代码如下。

模块声明:

Declare Function add Lib "C:\Program Files\RS\RSExternalInterface.dll" (ByVal Left As Integer, ByVal Right as Integer) As Integer

调用上面的函数,在一个按钮的点击事件子里面,如下:

result = add (5, 7)

执行上述行的那一刻,出现错误 49。

我尝试了以下方法:

  1. 删除了 VBA 函数声明中的“ByVal”
  2. 在 [DllExports ...] 之后删除并添加了 C# 函数中的装饰
  3. 确保我针对正确的 CPU 平台
  4. 删除了声明和调用中的参数,以确保 DLL 函数可访问。

我究竟做错了什么?

4

1 回答 1

0

我很惊讶问题标题和 C# 代码中的“调用约定”没有响起任何铃声(尽管我不排除MS方面的误导性错误消息)。CallingConvention

调用约定就像执行子程序时的协议,在:

  • callee - 例程(函数、过程(或不返回任何内容的函数,或返回void))
  • callermain -调用/执行被调用者的代码(也可以是子例程)

并确定:

  1. 谁在执行时处理(推送/弹出)堆栈上的函数参数(被调用者/调用者
  2. 堆栈上的参数顺序(右 -> 左左 -> 右

因为#1。,重要的是 2 方(被调用者/调用者在调用约定方面保持同步,否则他们会互相干扰,堆栈最终会损坏。“ Bad DLL Calling Convention ”就是这个意思:两者同步。

  • 对于没有参数的函数,您会得到正确的行为,因为没有任何东西可以推送/弹出堆栈 -这并不意味着没有任何错误;错误在那里,但没有遇到
  • 更多详细信息:[MSDN]:参数传递和命名约定。注意 和 之间的区别__stdcall__cdecl还要注意它们适用于32 位 ( x86 ) 架构AFAIK(也由错误备份),Excel可执行文件(作为整个MSOffice)是 32 位
  • 根据[MSDN]:DllImportAttribute.CallingConvention 字段(我假设它也适用于DllExport):

    您将此字段设置为CallingConvention枚举成员之一。CallingConvention 字段的默认值为Winapi,而后者又默认为StdCall约定。

我拿了你的代码,玩了一下。

代码.cs

using System;
using System.Runtime.InteropServices;
using RGiesecke.DllExport;


namespace RSExternalInterface
{
    public class RSExternalInterface
    {
        [DllExport("add")]
        public static int TestExport(int left, int right)
        {
            int ret = left + right;
            String s = String.Format("C# Func - add: {0} + {1} = {2}", left, right, ret);
            Console.WriteLine(s);
            return ret;
        }

        [DllExport("dbl", CallingConvention = CallingConvention.Winapi)]
        public static int TestExport1(int value)
        {
            int ret = value * 2;
            String s = String.Format("C# Func - dbl: {0} * 2 = {1}", value, ret);
            Console.WriteLine(s);
            return ret;
        }

        [DllExport("none", CallingConvention = CallingConvention.StdCall)]
        public static void TestExport2(int value)
        {
            String s = String.Format("C# Func - none: {0}", value);
            Console.WriteLine(s);
        }

    }
}

主.vb

Module main

    Declare Function add Lib "CsDll.dll" (ByVal Left As Integer, ByVal Right As Integer) As Integer
    Declare Function dbl Lib "CsDll.dll" (ByVal Value As Integer) As Integer
    Declare Sub none Lib "CsDll.dll" (ByVal Value As Integer)

    Sub Main()
        Console.WriteLine("64 bit OS: {0}{1}64 bit process: {2}{1}", Environment.Is64BitOperatingSystem, Environment.NewLine, Environment.Is64BitProcess)
        Dim i As Integer
        none(-7659)
        i = dbl(78)
        i = add(22, -13)

    End Sub

End Module

备注

  • 我无法在Excel中构建和运行它,所以我尝试了第二个最好的方法:从VB中执行。请注意,我对C#VB都没有经验
  • 一开始(正如我在评论中所说),我无法从C# .dll中导出任何内容。查看UnmanagedExports.nuspecnupkg的一部分):
    • 您必须将平台目标设置为 x86、ia64 或 x64。AnyCPU 程序集无法导出函数。
  • 平台任何 CPUVStudio默认)更改为x64(错误地),一切运行良好(尽管如此CallingConvention.Cdecl),只有在将其设置为x86后,我才能重现该问题
  • 无论如何,(C#)代码包含 3 种(不)指定调用约定的方法,这将在VB中工作

输出VStudio 2015(社区)):

64 bit OS: True
64 bit process: False

C# Func - none: -7659
C# Func - dbl: 78 * 2 = 156
C# Func - add: 22 + -13 = 9

这是一个(古老的)URL,它也提到了您的错误:[MSDN]: Bad DLL calling convention (Error 49)

于 2018-02-17T23:26:57.910 回答