Передать строку из неуправляемого кода в управляемый


У меня есть проблемы с передачей строки из неуправляемого кода в управляемый. В моем неуправляемом классе (unmanagedClass.cpp ) у меня есть указатель на функцию из управляемого кода:

TESTCALLBACK_FUNCTION testCbFunc;

Функция TESTCALLBACK_FUNCTION принимает одну строку и ничего не возвращает:

typedef void (*TESTCALLBACK_FUNCTION )(char* msg);
Неуправляемый класс наследуется от интерфейса ITest, который имеет только один метод:
    STDMETHOD(put_TestCallBack) (THIS_
                  LPVOID FnAddress       
             ) PURE;

В управляемом классе.cs я пишу такой код:

public class ManagedClass
{
    ITest unmanaged = new unmanagedClass();
    public delegate void TestDelegate(string info);
    ManagedClass()
    {
        unmanaged.put_TestCallBack(new TestDelegate(this.Test));
    }
    void Test(string info)
    {
            MessageBox.Show(info);
    }
}

[ComImport, Guid("<my guid here>")]
public class unmanagedClass
{
}

[ComImport, System.Security.SuppressUnmanagedCodeSecurity,
Guid("<my guid here>"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ITest
{
    [PreserveSig]
    int put_TestCallBack([MarshalAs(UnmanagedType.FunctionPtr), In] Capture.TestDelegate func);

}

Для вызова функции Test из неуправляемого кода я использую это

(*testCbFunc)("Func Uragan33::Execute has been started!");

Но когда тестовый метод из managedClass.cs называется I всегда получаемой нулевой строкой. Почему это происходит?

Заранее благодарю!

1 4

1 ответ:

У вас есть несоответствие в соглашении о вызове. Typedef в коде C++ объявляет указатель на функцию с соглашением о вызове по умолчанию, которое является _ _ cdecl. Но по умолчанию для делегата в управляемом коде используется _ _ stdcall.

Вам понадобится атрибут, чтобы сообщить Маршаллу pinvoke об обратном. Пусть это выглядит так:

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void TestDelegate(string info);

Отбросьте [MarshalAs] в объявлении функции. Исправление typedef в коде C++ может быть предпочтительнее, если вы можете, ясно делая все последовательное является предпочтительным решением:

    typedef void (__stdcall * TESTCALLBACK_FUNCTION )(char* msg);

Не связанный, это ошибка, которую вам нужно будет исправить:

   unmanaged.put_TestCallBack(new TestDelegate(this.Test));

Созданный объект делегата не виден сборщику мусора. Если будет собран на следующем GC, ваш код аварийно завершит работу, когда машинный код сделает обратный вызов. Вы должны хранить объект делегата где-то так, чтобы GC всегда видел ссылку. Либо как поле в классе, с дополнительным требованием, что объект класса должен оставаться живым достаточно долго, или в статической переменной.

Обратите внимание, как все эти проблемы исчезают, когда вы объявляете интерфейс обратного вызова вместо делегата. Способ связи.