как передать функцию обратного вызова C ++ в метод C # COM - PullRequest
0 голосов
/ 15 октября 2018

У меня есть COM-сервер, выполненный на C #, где один из методов принимает функцию обратного вызова.

   [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.AutoDual)]
    public class REDIServer
    {
        public bool Connect([MarshalAs(UnmanagedType.FunctionPtr)] ref CallBackFunction callback)
        {
            // send callback deeper into C# code
            return _service.Connect(callback);
        }

При генерации tlb-кода из этой dll, чтобы я мог использовать его в своем клиенте C ++,это выглядит так:

  virtual HRESULT __stdcall Connect (
    /*[in,out]*/ long * callback,
    /*[out,retval]*/ VARIANT_BOOL * pRetVal ) = 0;

И, наконец, когда я пытаюсь использовать вызов Connect () из C ++, я использую его так:

void Callback()
{
    cout << "got a callback\n";
}

int main()
{
    HRESULT hr = CoInitialize(NULL);

    try
    {
        REDIXLServer::_REDIServerPtr ptr(__uuidof(REDIXLServer::REDIServer));

        VARIANT_BOOL ret;
        hr = ptr->Connect((long *) &Callback, &ret);
    }
    catch (_com_error & e)
    {
        cout << e.ErrorMessage() << endl;
    }

Программа падает навызов Conect ().Я получаю окно сообщения «Помощник по управляемой отладке» InvalidFunctionPointerInDelegate обнаружил проблему .... Недопустимый указатель функции был передан во время выполнения для преобразования в делегат .... "

Итак, что я делаюнеправильно?Спасибо

1 Ответ

0 голосов
/ 16 октября 2018

Прежде всего, если вы взаимодействуете с собственными COM-клиентами, я бы предложил использовать COM-события вместо обратных вызовов

Кроме того, я бы предложил не использовать [ClassInterface(ClassInterfaceType.AutoDual)], поскольку он генерируетмного беспорядка в сгенерированных файлах tlh / tli.Вместо этого определите интерфейс и реализуйте его в своем классе.

public delegate double FunctionCallback(int arg);

[ComVisible(true)]
[Guid("5F11C485-0AA8-461D-BB56-32D620D17311")]
public interface ITestServer
{
  double Connect([MarshalAs(UnmanagedType.FunctionPtr)] FunctionCallback callback, int arg);
}

[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
public class TestServer : ITestServer
{ 
  public double Connect(FunctionCallback callback, int arg)
  {
    Console.Out.WriteLine("In server, calling callback...");
    return callback(arg);
  }  
}

Вам не нужно отправлять обратный вызов по ссылке (ref CallBackFunction), поскольку вы не будете изменять его, чтобы он указывал на другойреализации, и поскольку Callback совпадает с &Callback, вы все равно не можете разыменовать его - вот почему он падает.

В вашем родном клиенте обратный вызов будет смоделирован как long:

double Connect (long callback, long arg );

При отправке указателя функции просто приведите его к long:

double triple(int arg) {
    return arg * 3.0;
}

double halve(int arg) {
    return arg / 2.0;
}

int main() {
    CoInitialize(NULL);
    server::ITestServerPtr svr(__uuidof(server::TestServer));
    printf("tripled: %g\n", svr->Connect((long)triple, 5));
    printf("halved: %g\n", svr->Connect((long)halve, 5));
    return 0;
}
...