Параметры функции UnmanagedExports - причина VBA 49 Неверное соглашение о вызовах DLL
Я не могу заставить мой C# Dll работать с моей таблицей Excel Macro enable для вызова функции в Dll. Я могу вызвать функцию, если она не имеет параметров, способных вернуть ожидаемое значение. Но я не могу добиться успешного вызова Dll из VBA, когда добавляю входные параметры в функцию.
Общая сумма моего модуля 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
Вызов функции выше, внутри подпрограммы события click кнопки, выглядит следующим образом:
result = add (5, 7)
В момент выполнения вышеуказанной строки возникает ошибка 49.
Я пробовал следующее:
- Удалено "ByVal" в объявлении функции VBA
- Удалено и добавлено оформление в функции C# после [DllExports ...]
- Убедитесь, что я нацеливаюсь на правильную платформу процессора
- Удалены параметры в объявлении и вызове, чтобы гарантировать, что функция DLL доступна.
Что я делаю неправильно?
1 ответ
Я удивлен, что " Соглашение о вызовах " из названия вопроса и CallingConvention
из кода C# не прозвенел звонок (хотя я не исключаю вводящее в заблуждение сообщение об ошибке на стороне MS).
Соглашение о вызовах похоже на протокол при выполнении подпрограмм, между:
- вызываемый - подпрограмма (функция, процедура (или функция, которая ничего не возвращает или возвращает
void
)) - код вызывающего абонента (также подпрограмма - может быть
main
) который вызывает / выполняет вызываемый
и определяет:
- Кто обрабатывает (push / pop) аргументы функции в стеке (вызываемый / вызывающий) при его выполнении
- Порядок аргументов в стеке (справа -> слева или слева -> справа)
Из-за № 1. важно, чтобы обе стороны (вызывающий / вызывающий) были синхронизированы относительно соглашения о вызовах, в противном случае они будут мешать друг другу, и в конечном итоге стек будет поврежден. " Bad Calling Convention " означает именно это: 2 не синхронизированы.
- Вы получаете правильное поведение для функции без аргументов, потому что нечего толкать / вставлять в / из стека - это не означает, что ошибки нет; ошибка есть, но она не встречается
- Подробнее: [MSDN]: Соглашения о передаче аргументов и именовании. Обратите внимание на различия между
__stdcall
а также__cdecl
, а также обратите внимание, что они применяются к 32-битной (x86) архитектуре. AFAIK (также подкрепленный ошибкой), исполняемый файл Excel (как и весь MSOffice) является 32-битным - Согласно [MSDN]: поле DllImportAttribute.CallingConvention (которое, как я полагаю, также применимо к
DllExport
):Вы устанавливаете это поле для одного из членов перечисления CallingConvention. Значением по умолчанию для поля CallingConvention является Winapi, которое, в свою очередь, по умолчанию соответствует соглашению StdCall.
Я взял твой код и немного поиграл с ним.
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
Примечания:
- Я не смог собрать его и запустить в Excel, поэтому я попробовал 2- ую лучшую вещь: сделать это из VB. Обратите внимание, что у меня нет опыта ни в C#, ни в VB
- В начале (как я отмечал в комментарии) я не смог ничего экспортировать из C# .dll. Глядя на UnmanagedExports.nuspec (часть nupkg):
- Вы должны установить целевую платформу на x86, ia64 или x64. Сборки AnyCPU не могут экспортировать функции.
- Изменил платформу с любого процессора (по умолчанию VStudio) на 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]: неверное соглашение о вызовах DLL (ошибка 49)