Передача string из DLL (C#) в MetaTrader 4. Проблема с кодировкой?

 

Пробую писать DLL на C# для MetaTrader 4. Не могу передать string из DLL в MetaTrader. В DLL у меня:

        [DllExport(CallingConvention.StdCall)]
        public static string GetText()
        {
            return ("Good!");
        }

А в терминале получаю: 

test EURUSD,M1: GetText: ??!
Int передается корректно. Как правильно передать string?
 

Для обратной передачи строки (из DLL в MT) сначала нужно зарезервировать достаточное количество памяти для строки и передать указатель на нее:

string sReserve = "                                                                                                            ";
GetFromDLL(sReserve);

а в DLL (С++, для C# что-то примерно похожее) уже заполнить:

void GetFromDLL(wchar_t *sReserve)
{
        std::wstring wsMyString(_T("Good"));
        wcscpy(sReserve, wsMyString.c_str());
}

P. S. Писал "на коленке", по памяти. Мог в каких-то деталях ошибиться. Но общий смысл именно таков: вернуть указатель на строку из DLL нельзя, нужно подготовить память изначально.

 
Ihor Herasko #:

Для обратной передачи строки (из DLL в MT) сначала нужно зарезервировать достаточное количество памяти для строки и передать указатель на нее:

а в DLL (С++, для C# что-то примерно похожее) уже заполнить:

P. S. Писал "на коленке", по памяти. Мог в каких-то деталях ошибиться. Но общий смысл именно таков: вернуть указатель на строку из DLL нельзя, нужно подготовить память изначально.

Что-то я запутался. Сначала Вы говорите "передать указатель на нее", а потом " вернуть указатель на строку из DLL нельзя".

 
Ihor Herasko #:

Для обратной передачи строки (из DLL в MT) сначала нужно зарезервировать достаточное количество памяти для строки и передать указатель на нее:

а в DLL (С++, для C# что-то примерно похожее) уже заполнить:

P. S. Писал "на коленке", по памяти. Мог в каких-то деталях ошибиться. Но общий смысл именно таков: вернуть указатель на строку из DLL нельзя, нужно подготовить память изначально.

const string & по идее можно отдавать как скаляр ;-) то есть const wchar_t *GetFromDLL() ... тогда нет конфликтов за кто владелец памяти, строка просто статическая и жива покуда DLL не выгрузиться. На C# может даже прокатывать, но именно с const

хотя сам никогда так не делал, всегда: MQL готовит место, DLL туда копирует данные. Тогда всё гладко

 
Aleksandr Babitskii #:

Что-то я запутался. Сначала Вы говорите "передать указатель на нее", а потом " вернуть указатель на строку из DLL нельзя".

Указатель, который передается, указывает на память, принадлежащую эксперту. А вот вернуть указатель на строку, которая расположена вне памяти, принадлежащей эксперту, не получается.

 
Maxim Kuznetsov #:

const string & по идее можно отдавать как скаляр ;-) то есть const wchar_t *GetFromDLL() ... тогда нет конфликтов за кто владелец памяти, строка просто статическая и жива покуда DLL не выгрузиться. На C# может даже прокатывать, но именно с const

хотя сам никогда так не делал, всегда: MQL готовит место, DLL туда копирует данные. Тогда всё гладко

Сроковые литералы, это та еще тонкая тема в С/С++.

Какой будет правильный ответ на вопрос: "Что будет выведено на экран?"

#include <iostream>

const char *Get1() { return "qwerty"; }

const char *Get2() { return "qwerty"; }

bool Test(const char *a, const char *b) { return a == b; }

int main()
{
    std::cout << Test(Get1(), Get2()) << '\n';
}
 
Vladimir Simakov #:

Сроковые литералы, это та еще тонкая тема в С/С++.

Какой будет правильный ответ на вопрос: "Что будет выведено на экран?"

По идее - 0 (false), т. к. указатели разные.

P. S. Проверил. Результат 1. Получается, что произошло сравнение строк? Хотя, скорее всего, компилятор создал одну строку и дал один и тот же указатель на нее в обоих случаях. То есть все равно происходит сравнение указателей, а не содержимого строк.
 

Да, никакого сравнения строк здесь нет. Просто фичи компилятора.

Если изменить код на такой, то результат ожидаемый - 0:

#include <iostream>
#include <string>

std::wstring wc1(L"qwerty");
std::wstring wc2(L"qwerty");


bool Test(const wchar_t *a, const wchar_t *b) { return a == b; }

int main()
{
        std::cout << Test(wc1.c_str(), wc2.c_str()) << '\n';
}
 
Ihor Herasko #:

По идее - 0 (false), т. к. указатели разные.

P. S. Проверил. Результат 1. Получается, что произошло сравнение строк? Хотя, скорее всего, компилятор создал одну строку и дал один и тот же указатель на нее в обоих случаях. То есть все равно происходит сравнение указателей, а не содержимого строк.

1 потому что опция компилера "merge strings"..

 

А правильный ответ надо искать в стандарте.

Whether all string literals are stored in nonoverlapping objects and whether successive evaluations of a string literal yield the same or a different object is unspecified.

Так, что это unspecified behavior.

PS. Правда вероятность получить где-то результат отличный от true в одной единице трансляции исчезающе мала.

 

Я это к чему. Не надо строки гонять через нативный код. Если сильно надо получать их в dll, то делайте функцию size_t(wchar_t*,size_t) и заполняйте массив символов. Не помню, они гарантируют, что сигнатура импорта uint(string&,uint)/ulong(string&,ulong) правильно указатель на выделенную в string память передаст, если есть в справке такая гарантия, то юзайте, если нет, то костыль с CharArrayToString после заполнения массива.