Вызовите метод / функцию C # из C ++ DLL (которая загружается из C # с помощью «Dllimport») - PullRequest
2 голосов
/ 22 февраля 2012

Немного сложно возобновить его в одном заголовке, так что вот моя ситуация.

Я создаю приложение на C #, которое загружает библиотеку C ++.
Я вызываю функции из этой C ++ DLL.
Но я также хотел бы, чтобы моя C ++ DLL вызывала функции из приложения C # (то есть импортирует / запускает его) ...

Вот фрагмент кода, чтобы сделать его немного более полным:

// I'm importing functions from my native code
[DllImport(dllFilePath, CallingConvention = CallingConvention.Cdecl)]
public static extern int returnIntValue(int value);
[DllImport(dllFilePath, CallingConvention = CallingConvention.Cdecl)]
public static extern int returnIntArrValueAt(int[] values, int index);

// Here is the kind of code it should be
// [DllImport(dllFilePath, CallingConvention = CallingConvention.Cdecl)] 
// public static extern void callCSharpFunction( FunctionPointer fctPointer );

main    
{
    // I run the function
    int intValue1 = 
        MyAddIn.returnIntValue(147852369);
    int intValue2 = 
        MyAddIn.returnIntArrValueAt( new int[] { 9, 4, 3, 2, 1, 0 }, 5);

    // Here is an example function I use to call my C# func from C++
    // MyAddIn.returnIntValue( &myCSharpFunction );
}

// This is the method I'd like to call from my C++ imported library
static public void myCSharpFunction()
{
    MessageBox.Show("Called from C++ code !!");
}

Итак, резюме:

  • Код C # импортирует мою C ++ DLL
  • C # запускает функции из C ++ DLL
  • Метод C ++ DLL вызывает метод C #, который отображает MessageBox (т.е.)

Ответы [ 4 ]

3 голосов
/ 22 февраля 2012

Вот мой ответ на этот вопрос , который был похож.Мой пример не использует аргументы для обратного вызова.Так как ваши аргументы являются целыми числами, у вас не должно возникнуть никаких проблем.

По сути, метод Marshal.GetFunctionPointerForDelegate создает IntPtr из делегата.Этот делегат должен иметь ту же подпись, что и указатель функции, используемый в вашей библиотеке C ++.

// This is the delegate type that we use to marshal
// a function pointer for C to use. You usually need
// to specify the calling convention so it matches the
// convention of your C library. The signature of this
// delegate must match the signature of the C function
// pointer we want to use.
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void FunctionPointer();

// This is the function we import from the C library.
[DllImport(dllFilePath)]
static extern void callCSharpFunction(IntPtr fctPointer);

// This is the placeholder for the function pointer
// we create using Marshal.GetFunctionPointerForDelegate
IntPtr FctPtr;

// This is the instance of the delegate to which our function
// pointer will point.
FunctionPointer MyFunctionPointer;

// This calls the specified delegate using the C library.
void CallFunctionPointer(FunctionPointer cb)
{
    // make sure the delegate isn't null
    if (null == cb) throw new ArgumentNullException("cb");

    // set our delegate place holder equal to the specified delegate
    MyFunctionPointer = cb;

    // Get a pointer to the delegate that can be passed to the C library
    FctPtr = Marshal.GetFunctionPointerForDelegate(MyFunctionPointer );

    // call the imported function with that function pointer.
    callCSharpFunction(FctPtr);
} 
1 голос
/ 21 марта 2012

Итак, после нескольких тестов по возвращению к нулю мне наконец-то удалось запустить этот обратный вызов!

Итак, вот тестовый проект, который я создал для использования обратного вызова.

На стороне C ++

export.def

LIBRARY TestCallBack
    EXPORTS
        callCSharpFunction

TestCallBack.cpp

__declspec(dllexport) void callCSharpFunction ( void *fctPointer(int) )
{
    fctPointer(123456);
}

Этот проект C ++ создается как файл "DLL" и помещается в проект "lib" в папке проекта C #.

На стороне C #

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
    class Program
    {
        // Set the library path
        const string dllFilePath = 
        "C:\\PathToProject\\TestCallBack\\ConsoleApp\\lib\\TestCallBack.dll";

        // This is the delegate type that we use to marshal
        // a function pointer for C to use. You usually need
        // to specify the calling convention so it matches the
        // convention of your C library. The signature of this
        // delegate must match the signature of the C function
        // pointer we want to use.
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        delegate void FunctionPointer( int nb);

        // This is the function we import from the C library.
        //[DllImport(dllFilePath)]
        [DllImport(dllFilePath, CallingConvention = CallingConvention.Cdecl)]
        public static extern void callCSharpFunction(IntPtr fctPointer);

        // This is the placeholder for the function pointer
        // we create using Marshal.GetFunctionPointerForDelegate
        static IntPtr FctPtr;

        // This is the instance of the delegate to which our function
        // pointer will point.
        FunctionPointer MyFunctionPointer;

        // This calls the specified delegate using the C library.
        void CallFunctionPointer(FunctionPointer cb)
        {
            // make sure the delegate isn't null
            if (null == cb) throw new ArgumentNullException("cb");

            // set our delegate place holder equal to the specified delegate
            MyFunctionPointer = cb;

            // Get a pointer to the delegate that can be passed to the C lib
            FctPtr = Marshal.GetFunctionPointerForDelegate(MyFunctionPointer);

            // call the imported function with that function pointer.
            callCSharpFunction(FctPtr);
        } 

        static void Main(string[] args)
        {
            // This is the instance of the delegate to which our function
            // pointer will point.
            FunctionPointer printInConsoleDelegate;

            // Create the delegate object "MyFunctionPointer" that references 
            printInConsoleDelegate = new FunctionPointer(printInConsole);

            // Get a pointer to the delegate that can be passed to the C lib
            IntPtr printInConsolePtr = 
                Marshal.GetFunctionPointerForDelegate(printInConsoleDelegate);

            Console.WriteLine(
                "Call C++ which's calling back C# func \"printInConsole\"");

            // Call C++ which calls C#
            callCSharpFunction(printInConsolePtr);

            // Stop the console until user's pressing Enter
            Console.ReadLine();
        }

        public static void printInConsole(int nb) 
        {
            // Write the parameter in the console
            Console.WriteLine("value = " + nb + "; ");
        }
    }
}
1 голос
/ 22 февраля 2012

Это может быть сделано, и в целом не так уж и много.Однако есть несколько моментов для рассмотрения.

  • Поскольку вы сказали C ++, а не C, обратите внимание, что помимо статического класса, методов экземпляра, функций-друзей и т. Д., Есть еще некоторые функции, которые не являютсязагружается через DllImport из-за искажения имени.Во избежание COM обертывание C ++ в C иногда используется как более переносимая стратегия, позволяющая оборачивать библиотеки другими языками .Net или иным образом.Точно так же некоторые из этих же соображений применимы к обратным вызовам из обернутой библиотеки, как и в вашем случае.Хотя прототипы из вашей DLL, похоже, не являются проблемой, если они действительно были собраны с помощью компилятора C ++, возможно, стоит посмотреть на экспортированные имена символов, чтобы убедиться в этом.

  • Скажите, помогите с поиском, вам нужно знать словарный запас.Обычно, когда функция принимает в качестве параметра другую функцию, вызываемую либо во время вызова функции, либо позже, она называется callback .В мире C и C ++ это основано на функции языка, известной как указатели на функции (которые также служат целям, отличным от обратных вызовов).Часто в документации MS общий процесс предоставления возможности динамически связывать разные функции с разными вызывающими во время выполнения называется делегирование .C # -эквивалентом указателя на функцию являются объекты, известные как делегаты , созданные с помощью ключевого слова C # делегат .Я бы рекомендовал сначала создать несколько экспериментальных программ на C #, используя эту функцию, чтобы сначала понять, как это работает.

  • Также DllImport является частью реализации, фактическое средство .Net, которое вы используете, это pinvoke .Это следует также при поиске дополнительной информации.Затем вы захотите экспортировать свой делегат с помощью pinvoke путем маршалинга в качестве указателя на функцию.То есть .Net создаст функцию shim для запуска собственного кода.Две большие проблемные области, которые часто занимают несколько попыток: 1) убедиться, что сам делегат функции / маршалла имеет правильное соглашение о вызовах , и 2) что время жизни этой закулисной функции прокрутки равнотак что он все еще существует, когда его использует нативная DLL.Этот криволинейный шарик иногда может требовать ручного переопределения поведения сборки мусора, но обычно это означает просто сохранение ссылки на него.

  • Я действительно считаю, что Mono Documentation также лучше, чем MSDN в этой области.

0 голосов
/ 22 февраля 2012

Проект Unmanaged Exports может помочь вам в этом:
https://sites.google.com/site/robertgiesecke/Home/uploads/unmanagedexports

В основном это позволяет вам экспортировать функции из вашей сборки, используя атрибут DllExport. Эти функции могут затем использоваться как обычный собственный экспорт Dll. Это что-то вроде отсутствующего аналога атрибута DllImport.

Затем вы объявите свой метод следующим образом:

[DllExport("myCSharpFunction")]
static public void myCSharpFunction()
{
    MessageBox.Show("Called from C++ code !!");
}

Но и такая двусторонняя зависимость обычно выглядела бы мне подозрительно. Возможно, в вашем случае также возможно использовать обратные вызовы, как предложил ken.

...