Сложный технический вопрос по DLL - как передавать указатели на объекты?

 

Привет всем.

Пишу систему для взаимодействия с БД. Конечно же, без DLL никак не получается. Из DLL в роботов, написанных на MQL4, при вызове функции инициализации передаётся указатель на объект, который затем используется в других функциях. Вопрос как раз по передаче этого указателя. Каким образом можно передавать указатель между кодом на C++ и MQL4? Я не нашёл этого в справочниках.

У меня это сделано так. Указатель передаётся туда-обратно как тип int. Внутри функции на C++ после получения указателя на объект он преобразуется в int и передаётся в код на MQL4:

// Создаём объект MqlDll:

    MqlDll * dllObject = new MqlDll();


    // Возвращаем ссылку на полученный класс:
    return (int)dllObject;

Там он хранится до тех пор, пока не понадобится, а потом при вызове функции из DLL, работающей с объектом, передаётся в эту функцию. Внутри int обратно преобразуется в указатель, и выполняются какие-то действия с объектом, на который этот указатель указывает:

// Преобразуем переданный int в указатель на класс:
    MqlDll * dllClass = (MqlDll *)iPointer;
    // Проверяем версию БД и возвращаем результат проверки:

return dllClass->checkDBVer();

В основном всё работает. Но в некоторых случаях при запуске большого количества роботов, особенно на 64-битных системах, вдруг начинают сыпаться ошибки вроде "вызов функции завершился с ошибкой: критическая ошибка по адресу 0xC921AB223". Я предполагаю, что, возможно, размер указателя больше, чем int. В результате этого в некоторых случаях указатель обрезается при преобразовании туда-обратно и начинает указывать совсем не туда, куда планировалось. Поэтому думаю над использованием какого-нибудь другого типа данных. Но какой тип выбрать?

Пока что я думаю попробовать передавать указатель как тип string. Внутрь DLL он передаётся как char*, поэтому теоретически может вполне сгодится в качестве указателя. Но, может быть, при передаче идёт преобразование в строку - тогда всё будет ещё хуже. Как будет на самом деле - покажут тесты. Если string не покатит - тогда думаю попробовать double. В нём байтов больше, поэтому, возможно, при преобразовании указатели обрезаться не будут. Если и double не пойдёт - тогда придётся мириться с ошибками, устраняя их путём перезапуска терминала вместе с роботами.

Пробовал найти руководство, как правильно это сделать, но не получилось. Официальный учебник рассказывает только про передачу параметров по ссылке. Про передачу указателей, насколько я понял, там не говорится. Никакой статьи по этому вопросу также не нашёл. Поиск по форумам тоже в основном выдаёт результаты не по теме. Хотел написать в вопрос в техническую поддержку MetaQuotes - но не нашёл, как с ними связаться. Есть только ссылка на этот форум.

Может быть, кто-нибудь уже делал подобное? Может у кого-то есть пример, как это сделать правильно - поделитесь, пожалуйста. Также очень жду ответа сотрудников MetaQuotes. Раз у них на сайте указаны только ссылки на этот форум - надеюсь, они его читают.

 

0. Не надо придумывать фигню со строками-указателями! :-))

1. Тип указателя должен соответствовать разрядности проекта и ОС, где будет работать. INT нельзя применять на 64 разрядных ОС.

2. Есть специальные типы для указателей и размеров, которые сами переключаются куда надо в зависимоти от конфигурации проекта. Например, SIZE_T или DWORD_PTR.

3. Не надо передавать указатель в MQL. Оставте его внутри библиотеки. Сделайте интерфейс управления им через параметры функции.

 

size_t это тот же unsigned, размер как у инта ) а DWORD_PTR это дефайн, который тип указателя на тот же unsigned, которого в MQL ессно нету. В итоге как всегда из попытки выпендриться получается клоунада )

А насчет указателей правильно, лучше дескриптор передавать, а указатель держать у себя в укромном месте и брать по дескриптору когда надо.

Прямой ответ на прямой вопрос -- на 64х инта не хватит.

 

Какое-то мутное описание, но почти уверен что проблемы с выравниванием. В МКЛ оно == 1. Поэтому либо в мкл в ручную выравнивать сложные типы, либо в длл задавать выравнивание == 1. Как выглядит выровненная структура в мкл? Пример:

struct S
{
    char ch;
    int fill[7];    // заполнитель
    double d;
};

struct S  // Так писать структуры правильней
{
    double d;
    char ch;    
};

Место заполнителя думаю зависит от процессора (big endian или little endian).

 
Zhunko:

0. Не надо придумывать фигню со строками-указателями! :-))

1. Тип указателя должен соответствовать разрядности проекта и ОС, где будет работать. INT нельзя применять на 64 разрядных ОС.

2. Есть специальные типы для указателей и размеров, которые сами переключаются куда надо в зависимоти от конфигурации проекта. Например, SIZE_T или DWORD_PTR.

3. Не надо передавать указатель в MQL. Оставте его внутри библиотеки. Сделайте интерфейс управления им через параметры функции.


Спасибо за советы :)

Начнём по пунктам.

0. Я конечно понимаю, что фигню придумывать не надо :) Но придётся, если не получится сделать по уму :) Если не через строку - значит, как-то ещё. Если не получится - придётся оставлять так как есть (через инт). Это не лучший вариант, но по крайней мере он в большинстве случаев работает.

1. Я вообще думал, что MetaTrader 4 является 32-битным и в 64-разрядных ОС работает внутри виртуальной машины, которая делает для него соответствующие указатели. Потому и надеялся, что инты сгодятся. DLL тоже 32-битная.

2. Что там использовать внутри DLL - это не проблема.Там я могу использовать что угодно. Проблема в том, как это что-то передать наружу.

3. А вот это самый важный вопрос. Так сделать - наверное, это было бы лучше всего. Но каким образом можно так сделать? Я пока что не догадался.

Вот у меня есть база данных. Есть робот на MQL4. И есть DLL. Робот в начале своей работы хочет подключиться к БД. Он вызывает функцию подключения, которая возвращает указатель на полученный объект подключения. При вызове следующих функций, которые работают с подключением, им надо передать указатель на этот объект. Каким образом можно обойтись без того, чтобы передавать роботу из DLL полученный указатель, а затем передавать его обратно из робота в функции? Если не передавать его роботу - где его тогда хранить? У меня пока что нет идей, где внутри DLL можно было бы хранить указатель на созданный объект. Можете ли вы что-нибудь посоветовать? Я был бы очень благодарен.

 
TheXpert:

size_t это тот же unsigned, размер как у инта ) а DWORD_PTR это дефайн, который тип указателя на тот же unsigned, которого в MQL ессно нету. В итоге как всегда из попытки выпендриться получается клоунада )

А насчет указателей правильно, лучше дескриптор передавать, а указатель держать у себя в укромном месте и брать по дескриптору когда надо.


Не будете ли вы так добры рассказать насчёт дескрипторов и укромных мест подробнее? :) Я бы воспользовался этим способом.
 
CanSee:
Не будете ли вы так добры рассказать насчёт дескрипторов и укромных мест подробнее? :) Я бы воспользовался этим способом.

Создаем синглтон менеджер который хранит эти самые указатели

class Net;

class NetManager
{
public:
	typedef boost::shared_ptr<Net> NetPtr;

public:
	NetManager();
	~NetManager();

	bool RegisterUser(int userID, size_t inSize, size_t outSize, bool thresholds);
	bool UnregisterUser(int userID);

	NetPtr GetNet(int userID);

private:
	std::map<int, NetPtr> m_Nets;
	CRITICAL_SECTION m_Lock;
};

NetManager::NetManager()
{
        InitializeCriticalSection(&m_Lock);
}

NetManager& GetNetManager()
{
        static NetManager TheManager;
        return TheManager;
}

bool NetManager::RegisterUser(int userID, size_t inSize, size_t outSize, bool thresholds)
{
        std::map<int, NetPtr>::const_iterator it = m_Nets.find(userID);

        assert(it == m_Nets.end());
        if (it == m_Nets.end())
        {
                EnterCriticalSection(&m_Lock);
                
                NetPtr ptr(new Net(inSize, outSize, thresholds));
                m_Nets.insert(std::make_pair(userID, ptr));

                LeaveCriticalSection(&m_Lock);

                return true;
        }
        return false;
}

bool NetManager::UnregisterUser(int userID)
{
        EnterCriticalSection(&m_Lock);

        m_Nets.erase(userID);

        LeaveCriticalSection(&m_Lock);
        return true;
}

NetManager::NetPtr NetManager::GetNet(int userID)
{
        NetPtr ptr;

        std::map<int, NetPtr>::const_iterator it = m_Nets.find(userID);

        assert(it != m_Nets.end());
        if (it != m_Nets.end())
        {
                ptr = it->second;
        }

        return ptr;
}

NetManager::~NetManager()
{
        DeleteCriticalSection(&m_Lock);
}
У меня вот так незатейливо и просто. Еще и решает проблему одновременной работы кучи экспертов на нейронках. Только у меня указатели на сети, у вас свои. Когда MQL коду нужна дллка, он передает дескриптор (userID), по нему дллка из менеджера получает нужный указатель и производит необходимые действия. регистрация например в ините разрегистрация в деините.
 
220Volt:

Какое-то мутное описание, но почти уверен что проблемы с выравниванием. В МКЛ оно == 1. Поэтому либо в мкл в ручную выравнивать сложные типы, либо в длл задавать выравнивание == 1. Как выглядит выровненная структура в мкл? Пример:

Место заполнителя думаю зависит от процессора (big endian или little endian).


Спасибо за совет :) Однако я не понимаю, где здесь может быть проблема с выравниванием. Внутрь функций DLL из MQL4 передаются только обычные типы данных, хорошо описанные в учебнике (int, double, string), а не какие-то сложные структуры. Ну и этот самый указатель, тоже преобразованный к int. Обратно передаётся только int и string. Где здесь применять выравнивание?
 
TheXpert, спасибо большое! Буду пробовать применить это у себя.
 
TheXpert:

Создаем синглтон менеджер который хранит эти самые указатели

У меня вот так незатейливо и просто. Еще и решает проблему одновременной работы кучи экспертов на нейронках. Только у меня указатели на сети, у вас свои. Когда MQL коду нужна дллка, он передает дескриптор (userID), по нему дллка из менеджера получает нужный указатель и производит необходимые действия. регистрация например в ините разрегистрация в деините.
Не могу не добавить свое большое русское мерси. Не потому, что это мне нужно, а потому, что понравилось
 
CanSee:

Спасибо за совет :) Однако я не понимаю, где здесь может быть проблема с выравниванием. Внутрь функций DLL из MQL4 передаются только обычные типы данных, хорошо описанные в учебнике (int, double, string), а не какие-то сложные структуры. Ну и этот самый указатель, тоже преобразованный к int. Обратно передаётся только int и string. Где здесь применять выравнивание?


Тогда никаких проблем. А вот так адрес не передается?

#import "lib.dll"    // Новый мкл
    void fn(int &r);
#import
#import "lib.dll"    // Старый мкл
    void fn(int r[]);
#import