Как передать строку из C ++ - CLI в C # через C ++ - CLI обратные вызовы и делегаты - PullRequest
6 голосов
/ 26 апреля 2011

У меня есть оболочка C ++ - CLR вокруг стандартной библиотеки C ++, вызываемой из C #. Для получения сообщений о состоянии из библиотеки я использую делегата, назначенного для обратного вызова в коде C ++ через Marshal :: GetFunctionPointerForDelegate.

Это заняло у меня довольно много времени, чтобы начать работать, и я очень, очень близок (я думаю). Вызывается делегат C #, но строка неправильно передается через границу.

Когда я вызываю TakesCallback ("Test String") из кода C ++, я просто получаю мусор обратно в функцию C #.

--- Исходный класс C ++ и функция обратного вызова ---

class Solver
{
    private:

    std::string TakesCallback(const std::string message) 
    {
        return cb(message);
    }

    public:

    // Declare an unmanaged function type that takes a string
    typedef std::string (__stdcall* ANSWERCB)(const std::string);
    ANSWERCB cb;
};

--- Функция для установки обратного вызова из управляемой оболочки ----

// Set the delegate callback
void ManagedSolver::SetMessageCallback(SendMessageDelegate^ sendMessageDelegate)
{
    _sendMessage = sendMessageDelegate;

    // Use GetFunctionPointerForDelegate to get the pointer for delegate callback
    IntPtr ip = Marshal::GetFunctionPointerForDelegate(sendMessageDelegate);
    _solver->cb = static_cast<Solver::ANSWERCB>(ip.ToPointer());
} 

--- Функция C # передана в оболочку C ++ \ CLR SetMessageCallBack ----

private void Message(string message)
{
    XtraMessageBox.Show(message, "Done", MessageBoxButtons.OK);
}

Ответы [ 4 ]

3 голосов
/ 26 апреля 2011

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

Вы используете, например,

std::wstring s = clix::marshalString<E_UNICODE>(myCliString);

или

System::String^ s = clix::marshalString<E_ANSI>(mystdstring);

он работает в обоих направлениях и позволяет вам указать, какую кодировку вы хотите (ANSI, UTF8 или Unicode для Windows - на самом деле UTF16).

1 голос
/ 30 апреля 2013

C ++ (неуправляемый)

class DllContext
{
public:
typedef void (__stdcall *LOG_CALLBECK)(const wchar_t* LogString);
DllContext(LOG_CALLBECK WriteLog) // Constructor
{
    this->WriteLog = WriteLog;
    this->WriteLog(L"Creating сontext..."); // UNICODE string!
}

private:
LOG_CALLBECK WriteLog;
}

// Export function
SENDAUDIOCLIENT_API DllContext *CreateContext(DllContext::LOG_CALLBECK WriteLog)
{
    return new DllContext(WriteLog);
}

C #

class MyClass
{
private delegate void WriteLog_Delegate([MarshalAs(UnmanagedType.LPWStr)]string Mess);
private WriteLog_Delegate WriteLog;

[DllImport("My.dll", EntryPoint = "?CreateContext@@YAPAVDllContext@@P6GXPB_W@Z@Z", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateContext(WriteLog_Delegate WriteLogProc);

private IntPtr Context = IntPtr.Zero;

public MyClass()
{
    this.WriteLog = new WriteLog_Delegate(this.WriteLogToConsole);
        this.Context = CreateContext(this.WriteLog);
}

private void WriteLogToConsole(string Mess)
{
    Console.WriteLine("Message from unmanaged code: {0}", Mess);
}
}
1 голос
/ 26 апреля 2011

C ++ std::string и .NET System::String не являются взаимозаменяемыми.C # не может использовать первое, а собственный код C ++ не может использовать второе.Вам нужна функция C ++ / CLI, которая принимает std::string и преобразует ее в System::String^ перед вызовом делегата.

1 голос
/ 26 апреля 2011

В общем случае классы std c ++ нельзя маршалировать в / из C #.Если вы создаете свой код C ++ как Unicode, я бы порекомендовал изменить ваш код следующим образом:

C ++

// Declare an unmanaged function type that takes a string
typedef int (__stdcall* ANSWERCB)(LPCTSTR);

C #

private void Message([MarshalAs(UnmanagedType.LPWStr)]string message)
{
    XtraMessageBox.Show(message, "Done", MessageBoxButtons.OK);
}

Посмотритена следующем примере из MSDN

...