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:
- Who handles (push / pop) the function arguments on the stack (callee / caller) when executing it
- 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)