2010-07-22 2 views
3

У меня есть неуправляемая функция C++ со следующей подписью:Вызов функции C++ из 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 и нулевой для паров и 0 для ret являются допустимыми входами для функции. Когда я пытаюсь вызвать его, кажется, работает, однако при выходе я получаю следующее сообщение об ошибке:

PInvokeStackImbalance was detected

A call to PInvoke function '...::Invoke' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.

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

+0

Нужно ли импортировать его в качестве делегата? Обычная декларация «статического extern» была бы намного проще. –

+0

@ Хенк Холтерман: Полагаю, что нет, я получил этот код от коллеги, я предположил, что у него есть причина для этого. Тем не менее, я должен динамически загружать функцию во время выполнения, не зная имя (путь) или имя функции.Однако я знаю подпись функции во время разработки. Это можно сделать по-другому? –

+0

«Динамично» - хорошая причина, и я думаю, что ответы @leppies - это то, куда вам нужно идти. –

ответ

2

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

Прошу прощения, но мне нужна помощь в понимании кода. Где мой DLL-файл (или путь к нему) и имя функции? –

+0

@Rekreativc: Как и вы. Результат моего метода отправляется при извлечении указателя функции. Тем не менее, он будет иметь тип Delegate, и вам нужно будет вызвать на нем 'DynamicInvoke'. Вы могли бы сделать еще одну оболочку Reflection.Emit, чтобы вызвать ее напрямую. – leppie

+1

@Rekreativc: Да, тогда на результат вызывается 'DynamicInvoke'. – leppie

-1

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

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

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

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

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

С небольшим количеством Googling из этого вы должны быть в состоянии понять это.

+0

Проблема заключается в том, что вы не можете указать соглашение о вызове для делегата на C#, и именно это вызывает проблему. – leppie

7

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

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

От MSDN:

Controls the marshaling behavior of a delegate signature passed as an unmanaged function pointer to or from unmanaged code.

+1

+1: Спасибо, этот ** решил ** проблемы для меня с 'typedef void (* InvokeCallack) (void * opaque);' определение обратного вызова. Фактический обратный вызов был скомпилированным методом C++. Я определил в C++/CLI делегат с подписью 'delegate void InvokeCallack (IntPtr opaque);' с вашим атрибутом '[UnmanagedFunctionPointer (CallingConvention :: Cdecl)]', и теперь у меня нет проблем с вызовом делегата в потоке пользовательского интерфейса используя Control :: BeginInvoke. Как ни странно, тот же код работал в VS2008 без атрибута. В VS2010 требуется атрибут. – ceztko

Смежные вопросы