Вызов функции C ++ / CLI из библиотеки C ++ - PullRequest
2 голосов
/ 21 января 2010

Мне нужно интегрировать нативную библиотеку C ++ в проект C #. Теперь в этой библиотеке C ++ есть класс с виртуальными функциями, которые мне нужно наследовать в C ++ / CLI. Так что в C ++ / CLI я написал что-то вроде

class C++CliClass : public C++Class
{ 
  C++CliClass(Callback callback) { iCallback = callback; }
  virtual VirualFunctionCallFromC++(int x, int y, int z, SomeClass *p)
  { 
     // I need to call C++/CLI here
     iCallback(x, y, z, p);
  }

 private:
   Callback iCallback;
}

I defined the callback function as:
typedef int (__cdecl *Callback)(int x, int y, int z, SomeClass *p);

The idea is now that C++ library calls the virtual function of the C++Cli 
    class which on his turn calls the call back which gets me hopefully into C#.

// This delegate definition is needed to setup the callback for the C++ class
delegate int CallbackDelegate(int x, int y, int z, SomeClass *p);

So now I defined a managed C++/CLI class
public ref class GCClass
{
  public: 
    delegate <Byte>^ GetDataDelegate();
    GCClass(GetData^ getDataDelegate) { iGetDataDelegate = getDataDelegate };

  private:
    GetDataDelegate ^iGetDataDelegate;
    int GetData(int x, int y, int z, SomeClass *p)
    {
       // call delegate into C#
       <Byte>^ data = iGetDataDelegate->Invoke();
    }
  public:
    void SomeFunctionWhichEventuallyCallsC++Libary
    {
       // create a delegate for the method that will call the C# delegate
       CallbackDelegate ^d = gcnew CallbackDelegate(this, &GCClass::GetData);
       IntPtr del = System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(d);

       // Install in the C++ class
       C++CliClass(del.ToPointer());

       // Setup the C++ library and install my C++ class into the library
       SomeObjectOfTheLibrary->Install(&C++CliClass);
       SomeObjectOfTheLibrary->DoSometing() // will call the C++ virtual function and end up in C#

       // The delegate is no longer needed anymore
    }

Пока здесь код. Так что я надеялся достичь того, что кто-то может вызвать метод моего управляемого класса C ++ / CLI, который использует собственную библиотеку C ++ для своей работы. В свою очередь библиотека C ++ вызывает обратный вызов C ++ / CLI и, наконец, вызывается делегат C #. Теперь, наконец, вопрос: все отлично работает в режиме отладки. Однако в режиме выпуска иногда генерируется AccesException, а иногда просто зависает приложение. Я подозреваю, что это как-то связано с различными соглашениями о вызовах для C ++ / CLI и C ++. Например, я заметил, что во время второго вызова обратного вызова значение iCallback отличается от первого вызова. Однако для всех последующих вызовов значение iCallback больше не меняется. Я ожидаю, что значение iCallback всегда должно быть одинаковым, но я не уверен, потому что я не знаю, как внутренне работает фреймворк, чтобы иметь возможность вызывать делегата из C ++. Я также попытался определить соглашение о вызовах CallbackDelegate с помощью [UnmanagedFunctionPointer (Cdecl)]. Я перепробовал все варианты, но безрезультатно: я всегда получаю исключение или приложение зависает навсегда. Может кто-нибудь подсказать, что может быть не так?

Ответы [ 2 ]

2 голосов
/ 21 января 2010

Одна из проблем с вашим кодом выше состоит в том, что d является управляемой ссылкой, что означает, что ее можно перемещать во время выполнения. Это в свою очередь сделает недействительным ваш указатель обратного вызова.

Чтобы передать делегат в собственный код, необходимо указать сборщику мусора не перемещать его, используя GCHandle::Alloc:

   CallbackDelegate^ d = gcnew CallbackDelegate(this, &GCClass::GetData);

   // As long as this handle is alive, the GC will not move or collect the delegate
   // This is important, because moving or collecting invalidate the pointer
   // that is passed to the native function below
   GCHandle delegate_handle = GCHandle::Alloc(d);

   IntPtr del = System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(d);

   C++CliClass native(del.ToPointer());

   SomeObjectOfTheLibrary->Install(&native);
   SomeObjectOfTheLibrary->DoSometing() // will call the C++ virtual function and end up in C#

   // Release the handle
   delegate_handle.Free();
2 голосов
/ 21 января 2010

Убедитесь, что делегат не является сборщиком мусора, когда он все еще необходим. В классе C ++ CliClass вы можете добавить член типа CallbackDelegate и установить для него значение d. Если экземпляр C ++ CliClass существует только во время выполнения SomeFunction. Может быть достаточно GC.KeepAlive (d) в конце функции.

Возможно, даже проще: в C ++ CliClass определите член типа gcroot , а затем напрямую вызовите функцию GetData для этого члена в VirtualFunction без необходимости делегата.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...