Вызов функции C ++ из C # - несбалансированный стек - PullRequest
4 голосов
/ 22 июля 2010

У меня есть неуправляемая функция C ++ со следующей подписью:

int function(char* param, int ret)

Я пытаюсь позвонить из C #:

unsafe delegate int MyFunc(char* param, int ret);

...

int Module = LoadLibrary("fullpathToUnamanagedDll");
IntPtr pProc = GetProcAddress(Module, "functionName");
MyFunc func = (MyFunc)System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer(pProc, typeof(MyFunc));

unsafe
{
    char* param = null;
    int ret = 0;
    int result = func(param, ret);
}

Насколько я могу судить из старой спецификации проекта C ++, оба значения null для param и 0 для ret являются допустимыми входными данными для функции. Когда я пытаюсь вызвать его, кажется, что он работает, однако при выходе я получаю следующую ошибку:

Обнаружен дисбаланс PInvokeStack

вызов функции PInvoke '... :: Invoke' разбалансировал стек. Это скорее всего потому, что управляемый ПИнвок подпись не соответствует неуправляемой целевая подпись. Проверьте, что соглашение о вызовах и параметры подпись PInvoke соответствует цели неуправляемая подпись.

Я попробовал почти все, что мог придумать (небезопасно было в крайнем случае), однако я не могу найти способ запустить функцию без получения несбалансированного стека. Могу я попробовать что-нибудь еще?

Ответы [ 3 ]

8 голосов
/ 16 сентября 2011

Я знаю, что этому вопросу уже год, но более простой способ, чем динамическое построение типа, - объявить соглашение о вызовах с помощью атрибута UnmanagedFunctionPointer в вашем делегате, например:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
unsafe delegate int MyFunc(char* param, int ret);

С MSDN :

Управляет маршалингом подписи делегата, передаваемой как указатель неуправляемой функции на или из неуправляемого кода.

2 голосов
/ 22 июля 2010

IIRC, вам необходимо украсить подпись делегата соглашением о вызовах.К сожалению, это можно сделать только через IL или сгенерировать заглушку с помощью Reflection.Emit.

Вы можете попробовать это:

protected static Type MakeDelegateType(Type returntype, List<Type> paramtypes)
{
  ModuleBuilder dynamicMod = ... ; // supply this

  TypeBuilder tb = dynamicMod.DefineType("delegate-maker" + Guid.NewGuid(), 
      TypeAttributes.Public | TypeAttributes.Sealed, typeof(MulticastDelegate));

  tb.DefineConstructor(MethodAttributes.RTSpecialName | 
       MethodAttributes.SpecialName | MethodAttributes.Public |
       MethodAttributes.HideBySig, CallingConventions.Standard,
       new Type[] { typeof(object), typeof(IntPtr) }). 
       SetImplementationFlags(MethodImplAttributes.Runtime);

  var inv = tb.DefineMethod("Invoke", MethodAttributes.Public | 
       MethodAttributes.Virtual | MethodAttributes.NewSlot | 
       MethodAttributes.HideBySig, 
       CallingConventions.Standard ,returntype,null, 
       new Type[] 
       { 
          // this is the important bit
          typeof(System.Runtime.CompilerServices.CallConvCdecl)
       }, 
       paramtypes.ToArray(), null, null);

  inv.SetImplementationFlags(MethodImplAttributes.Runtime);

  var t = tb.CreateType();
  return t;
}
0 голосов
/ 22 июля 2010

Необходимо помнить о двух вещах: соглашение о вызовах между битом C # и вашей DLL и как данные char * распределяются через этот интерфейс. Если вы ошибетесь, вы получите жалобы на повреждение стека. При определении вашего интерфейса гораздо проще, если вы можете ограничить размер вашего блока данных чем-то фиксированным, т.е. установить максимальную длину строки.

Вот статическая версия, в которой имя DLL является фиксированным, а ваша строка обрабатывается как байт [] и имеет размер не более 2 КБ, и вы можете определить динамическую версию из этого:

    private const string MYDLL = @"my.dll";

    [DllImport(MYDLL, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)]
    public static extern int DataBlockDownload([MarshalAs(UnmanagedType.U4)] int A, [MarshalAs(UnmanagedType.LPArray, SizeConst = 2048)] byte[] B, [MarshalAs(UnmanagedType.U4)] int C);

    // NOTE: The data block byte array is fixed at 2Kbyte long!!
public delegate int DDataBlockCallback([MarshalAs(UnmanagedType.U4)] int A, [MarshalAs(UnmanagedType.LPArray, SizeConst = 2048)] byte[] B, [MarshalAs(UnmanagedType.U4)] int C);

Вы также можете указать используемый набор символов, если хотите использовать типы символов, как указано выше.

Вы не говорите, что делаете со своими символами *, если они входят в ваш код C ++ в качестве параметра или если код C ++ передает их обратно в управляемый мир. Прочтите ключевые слова C # ref и out как способы избежать типа char * и модификатора unsafe.

Немного погуглив, вы сможете это понять.

...