2

I am unable to get my C# Dll to work with my Excel Macro enable spreadsheet to call the function in the Dll. I am able to call the function if it has no parameters, able to return the expected value. But I cannot get the Dll call from VBA to be successful when I add input parameters to the function.

The sum total of my C# module is as follows:

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;
        }
    }
}

My VBA Code is as follows.

Module Declaration:

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

Call the function above, inside the click event sub of a button, is as follows:

result = add (5, 7)

The moment the above line is executed, Error 49 comes up.

I have tried the following:

  1. Removed the "ByVal" in the VBA function declaration
  2. Removed and added the decoration in the C# function, after [DllExports ...]
  3. Ensured that I target the correct CPU platform
  4. Removed the parameters in the declaration and calling to ensure that the DLL function is accessible.

What am I doing wrong?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • You may need to type `Lib` after `add` – K.Dᴀᴠɪs Jan 25 '18 at 20:58
  • The Lib is indeed in my code. Typo for not adding it in above. Corrected. – Willie Marais Jan 25 '18 at 21:09
  • Does it work if you remove `As Integer` from your declarations? Just out of curiosity. – K.Dᴀᴠɪs Jan 25 '18 at 21:35
  • Check your *C#* *.dll* with [Dependency Walker](http://www.dependencywalker.com), and see if the exported function(s) is/are shown. I copy/pasted your exact code, but my *.dll* doesn't seem to export anything. Loading the func from *VBasic* fails (obviously), as it can't be found. – CristiFati Feb 17 '18 at 11:32

1 Answers1

0

I am surprised that "Calling Convention" from question title and CallingConvention from C# code didn't ring any bell (although I don't rule out a misleading error message on MS side).

Calling convention is like a protocol when executing subroutines, between:

  • callee - the routine (function, procedure (or function that doesn't return anything, or returns void))
  • caller - code (a subroutine as well - could be main) that calls/executes the callee

and determines:

  1. Who handles (push / pop) the function arguments on the stack (callee / caller) when executing it
  2. Arguments order on the stack (right -> left or left -> right)

Because of #1., it's important that the 2 parties (callee / caller) to be in synch regarding calling convention, otherwise they will setp on each other's toes, and the stack will eventually get corrupted. "Bad DLL Calling Convention" means exactly this thing: the 2 are not in synch.

  • You get correct behavior for function without arguments, because there's nothing to push / pop to/from the stack - this doesn't mean that there isn't any error; the error is there but it's not encountered
  • More details: [MSDN]: Argument Passing and Naming Conventions. Note differences between __stdcall and __cdecl, and also note that they apply to 32bit (x86) architecture. AFAIK (also backed up by the error), Excel executable (as whole MSOffice) is 32bit
  • According to [MSDN]: DllImportAttribute.CallingConvention Field (which I assume also applies to DllExport):

    You set this field to one of the CallingConvention enumeration members. The default value for the CallingConvention field is Winapi, which in turn defaults to StdCall convention.

I took your code, and played a little bit with it.

code.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);
        }

    }
}

main.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

Notes:

  • I wasn't able to build it and run it in Excel, so I tried the 2nd best thing: do it from VB. Note that I'm experienced in neither C# nor VB
  • At the beginning (as I stated in a comment), I wasn't able to export anything from the C# .dll. Looking at UnmanagedExports.nuspec (part of the nupkg):
    • You have to set your platform target to either x86, ia64 or x64. AnyCPU assemblies cannot export functions.
  • Changed the Platform from Any CPU (VStudio default) to x64 (by mistake), and everything ran fine (in spite of CallingConvention.Cdecl), only after setting it to x86 I was able to reproduce the problem
  • Anyway, the (C#) code contains 3 ways of (not) specifying a calling convention, that will work from VB

Output (VStudio 2015 (Community)):

64 bit OS: True
64 bit process: False

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

Here's an (ancient) URL that also mentions your error: [MSDN]: Bad DLL calling convention (Error 49)

CristiFati
  • 38,250
  • 9
  • 50
  • 87