Russian Qt Forum
Октябрь 03, 2024, 09:31 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Загрузка DLL слинкованной в Delphi (Access Violation)  (Прочитано 12162 раз)
QuAzI
Гость
« : Февраль 28, 2012, 12:19 »

Хоть через QLibrary, хоть через WINAPI LoadLibrary получаю одно и то же: приложение падает сразу после обращения к "полученным" из dll данным (конкретно на первой строке после qDebug() << "parse GetDLLInfo"; )
Если на виртуальной машине и из под отладчика Qt ещё прокатило натыкать qDebug() и кое как оно работало, то на реальной машине не получается выдернуть текст из GetDLLInfo ни из Qt-шной dll, ни из Delphi-шной.
Код:
{
    QStringList nameFilters;
    nameFilters << "ss3*.dll";

    QDir mainDir(qApp->applicationDirPath());
    QStringList files = mainDir.entryList(nameFilters, QDir::Files, QDir::Name);

//    QLibrary *myLib = new QLibrary();

    for (int i=0; i<files.count(); i++)
    {        
        HINSTANCE libHandle = LoadLibrary(QString(files.at(i)).toStdWString().c_str());
        if (libHandle)
        {
            typedef const char* (*MyPrototype)();
            qDebug() << "beforeResolved" << files.at(i);
//            MyPrototype myFunction = (MyPrototype) myLib->resolve(files.at(i), "GetDLLInfo");
            MyPrototype myFunction = (MyPrototype) GetProcAddress(libHandle, "GetDLLInfo");
            qDebug() << "afterResolved";
            if (myFunction)                
            {                                
                qDebug() << "functionDetected";
                const char* str = myFunction();                                
                if (str)
                {
                    qDebug() << "parse GetDLLInfo";
                    short sz =  static_cast<unsigned short>(str[0]);
                    qDebug() << "str size= " << sz;
                    QByteArray encodedString(str +1, sz);
                    qDebug() << "str = " << encodedString;
                    QTextCodec *codec = QTextCodec::codecForName("Windows-1251");
                    QString buff = codec->toUnicode(encodedString) ;
                    qDebug() << files.at(i) << sz << buff;
                    ui->textEdit->append( files.at(i) + "  : GetDLLInfo("+ QVariant(sz).toString() +") :  " + buff );                    
                }
            }
            qDebug() << "afterParse";
        }
    }
}
В Delphi функция объявлена так
Код:
function GetDLLInfo: ShortString; StdCall;
...
implementation

function GetDLLInfo: ShortString;
begin
 Result:=sDLLInfo;
end;
В DLL на Qt пока сделал так
Код:
#ifdef D_SHARED_LIB
        #define D_SHARED extern "C" __declspec(dllexport) Q_DECL_EXPORT
#else
        #define D_SHARED extern "C" __declspec(dllexport) Q_DECL_IMPORT
#endif

D_SHARED char* GetDLLInfo();

...

char* GetDLLInfo()
{
    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
    QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
    QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));

qInstallMsgHandler(myMessageOutput);

qDebug() << "GetDLLInfo";

QString tmp = "Типа плугин";

 QTextCodec *codec = QTextCodec::codecForName("Windows-1251");

 QByteArray text = codec->fromUnicode( tmp.toStdString().c_str() );
 char *data = new char[text.size() + 2];
 strcpy(data+1, text.data());
 data[0] = static_cast<unsigned short>(text.size());

    return data;
}
И если тестовая DLL из Delphi грузится в целевой проект (который тоже накорябан на Delphi), то писанная на Qt им упорно отпинывается. Жаль только исходников к нему нет и потому логов от него добиться вообще не получается.

Кувыркаюсь с этим: Qt SDK 2010.5; Qt Creator 2.0.1 ; Qt 4.7.0 и его родной MinGW (не перелинковывал под MS VC)
« Последнее редактирование: Февраль 28, 2012, 12:24 от QuAzI » Записан
mutineer
Гость
« Ответ #1 : Февраль 28, 2012, 12:22 »

Припиши явно к указателю на функцию __stdcall
Записан
QuAzI
Гость
« Ответ #2 : Февраль 28, 2012, 12:32 »

После указания __stdcall в экспорте имён появляются @<длинна входных параметров> и с такими именами во первых в упор DLL не грузит дельфовый проект, во вторых довольно смутно представляю, как потом самому по именам через QLibrary без указания всей белиберды за знаком "@" грузить.
Записан
mutineer
Гость
« Ответ #3 : Февраль 28, 2012, 12:35 »

Ну соглашение вызова в любом случае надо явно указывать и при экспорте и при импорте. А вот с именами гугл поможет как никто другой
Записан
QuAzI
Гость
« Ответ #4 : Февраль 28, 2012, 12:39 »

Беда в том, что не помог (хотя в английском я тоже не силён). Как раз и читал про то, что в зависимости от соглашений вызова будет разное именование в экспортах. И как сменить именование не нашёл. Странно, что в Delphi без бубнов получаются такие "красивые" имена.
Записан
mutineer
Гость
« Ответ #5 : Февраль 28, 2012, 12:43 »

Везде могут получиться такие имена, насколько я понял это от соглашения вызова зависит. Странно, другим гугл в этом помог
Записан
QuAzI
Гость
« Ответ #6 : Февраль 28, 2012, 12:47 »

Гугль то помог... да не там... если бы речь стояла о том, как мне из моего приложения юзать собственную функцию, вопроса бы небыло... а сейчас по сути моё приложение - это только тестер для DLL пишущейся для чужого приложения. И я не могу менять имена.

Переписал, теперь оно дёргает и GetDLLInfo@0 через __stdcall и GetDLLInfo. Теперь моя кривулька на Qt возвращает тестовый текст... а писанный на Delphi вариант (который на ура определяется прогой для которой вся эта кухня и делается) - фигушки. Адрес по GetDLLInfo получает, но опять таки после обращения в туда - валится всё приложение.
Код:
{
    QStringList nameFilters;
    nameFilters << "ss3*.dll";

    QDir mainDir(qApp->applicationDirPath());
    QStringList files = mainDir.entryList(nameFilters, QDir::Files, QDir::Name);

//    QLibrary *myLib = new QLibrary();

    for (int i=0; i<files.count(); i++)
    {       
//        myLib->setFileName( files.at(i) );
//        if (myLib->load())
        HINSTANCE libHandle = LoadLibrary(QString(files.at(i)).toStdWString().c_str());
        if (libHandle)
        {
            typedef __stdcall const char* (*MyPrototype)();
            qDebug() << "beforeResolved" << files.at(i);
//            MyPrototype myFunction = (MyPrototype) myLib->resolve(files.at(i), "GetDLLInfo");
            MyPrototype myFunction = (MyPrototype) GetProcAddress(libHandle, "GetDLLInfo@0");
            qDebug() << "afterResolved";

            if (!myFunction)
            {
                qDebug() << "Using GetDLLInfo";
                myFunction = (MyPrototype) GetProcAddress(libHandle, "GetDLLInfo");
            } else qDebug() << "Using GetDLLInfo@0";

            if (myFunction)
            {                               
                qDebug() << "functionDetected";
                const char* str = myFunction();                               
                if (str)
                {
                    qDebug() << "parse GetDLLInfo";
                    short sz =  static_cast<unsigned short>(str[0]);
                    qDebug() << "str size= " << sz;
                    QByteArray encodedString(str +1, sz);
                    qDebug() << "str = " << encodedString;
                    QTextCodec *codec = QTextCodec::codecForName("Windows-1251");
                    QString buff = codec->toUnicode(encodedString) ;
                    qDebug() << files.at(i) << sz << buff;
                    ui->textEdit->append( files.at(i) + "  : GetDLLInfo("+ QVariant(sz).toString() +") :  " + buff );                   
                }
            }
            qDebug() << "afterParse";
        }
//        else qDebug() << "Can't load DLL: " << files.at(i);
                    /* if (myLib->isLoaded())
                    {
                        myLib->unload();
                    } */
    }
}
Записан
QuAzI
Гость
« Ответ #7 : Февраль 28, 2012, 13:24 »

Если внимательно прочесть код, то можно увидеть что в этом самом массиве нулевой символ как раз длинной и подменяется.
Хотя если покажете более красивое/правильное решение с этой структуркой, буду признателен.
Записан
QuAzI
Гость
« Ответ #8 : Февраль 28, 2012, 14:19 »

Опа, супер, наконец-то это как-то да зашевелилось. Дельфовое приложение схавало DLL с этим:
Код:
struct ShortString {
    unsigned char Length;
    char Data[255];
};

D_SHARED __stdcall ShortString GetDLLInfo();

...

// function GetDLLInfo: ShortString;
ShortString GetDLLInfo()
{
    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
    QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
    QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));

qDebug() << "GetDLLInfo";

ShortString st;

QString tmp = "Типа плугин";

 QTextCodec *codec = QTextCodec::codecForName("Windows-1251");

 QByteArray text = codec->fromUnicode( tmp.toStdString().c_str() );
 char *data = new char[text.size() + 2];
 strcpy( st.Data, text.data());
 st.Length = text.size();

    return st;
}
Спасибо.

Кстати вроде и с def-файлами маленько разобрался. В PRO-файл кинул
Код:
DEF_FILE += ss3plugin-qt-example.def
Сам DEF в директории сборки
Код:
EXPORTS    GetDLLInfo          
Оно линкуется, но в DLL в итоге попадают ОБА наименования, GetDLLInfo и GetDLLInfo@0. Ну хоть работает маленько и то добро.
« Последнее редактирование: Февраль 28, 2012, 14:23 от QuAzI » Записан
QuAzI
Гость
« Ответ #9 : Февраль 28, 2012, 15:17 »

AnsiString в проектине Qt теперь получаю так. Вроде всё красиво.
Код:
            typedef __stdcall const ShortString (*MyPrototype)();
            MyPrototype myFunction = (MyPrototype) GetProcAddress(libHandle, "GetDLLInfo");

            if (myFunction)
            {                                               
                const ShortString str = myFunction();
                if (str.Length>0)
                {
                    short sz = str.Length;
                    QByteArray encodedString(str.Data, str.Length);
Теперь воюю с передачей параметров не из, а в DLL.
В оригинале было так
procedure ShowFormFromDLL(AppHandle: THandle; DBName, LibName: PChar); StdCall;
У себя я это экспортирую так
D_SHARED __stdcall void ShowFormFromDLL(HINSTANCE AppHandle, char* DBName, char* LibName);
И собственно сама функция:
Код:
void ShowFormFromDLL(HINSTANCE AppHandle, char* DBName, char* LibName)
{
    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
    QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
    QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));   

    HINSTANCE GlobHinstDLL = AppHandle;

    int argc = 0;char* argv[1];
    argv[0]="ss3plugin-qt-example1.dll";
    QApplication* pApp = new QApplication(argc, argv);

    QString m_DBName = DBName, m_LibName = LibName;

        Widget w(m_DBName, m_LibName);
        w.show();

        pApp->exec();
    qDebug() << "ShowFormFromDLL" << DBName << LibName;
}
Т.е. берём хэндл "родителя", ещё пару параметров, создаём, рисуем виджет и на него кидаем DBName и LibName, чтобы убедиться, что они нормально прилетели. Всё нормально передаётся-рисуется, но при закрытии этого плагина-виджета падает родительское приложение. Неужели и тут где-то стэк надо подчищать?
Записан
QuAzI
Гость
« Ответ #10 : Февраль 28, 2012, 15:20 »

Разобрался. Надо было прибить QApplication.
Записан
QuAzI
Гость
« Ответ #11 : Февраль 29, 2012, 16:29 »

Рано радовался. Из приложения Delphi создал свой QApplication, побаловался, убил. Всё хорошо. А если грузить эту же DLL из Qt-шного графического приложения, то виджет только мигнуть успевает и сразу исчезает. Разумеется не отработав. Оно понятно, что QApplication должно быть одно для всего приложения и его библиотек, но не понятно, как с ним воевать. Сейчас пытаюсь воевать так
Код:
    qDebug() << argv[0] << "ShowFormFromDLL" << DBName << LibName;

    /// Создаём при необходимости свой экземпляр QApplication, без него не работает GUI
    bool mainQApp = false;
    QApplication* pApp = (QApplication*) qApp->instance();
            if (!pApp)
            {
                pApp = new QApplication(argc, argv);
                mainQApp = true;
            }

        Widget w(m_DBName, m_LibName);
        w.show();

        if (mainQApp) {
        pApp->exec();
            delete qApp; /// Удаляем свой экземпляр QApplication
        }
Записан
QuAzI
Гость
« Ответ #12 : Март 01, 2012, 01:32 »

Забавно что при использовании QxtApp поведение виджета идентично. По логам (перенаправил qDebug и qWarning в файл) вижу что отработали конструктор и деструктор виджета, а между ними выскакивает qWarning о том что QApplicationCore::EventFilter уже проинсталлирован.
Записан
QuAzI
Гость
« Ответ #13 : Март 03, 2012, 16:19 »

В аттаче полные исходники тестера и плагина.
Плагин выбивает если раскомментировать в main.cpp строку 87, в которой виджет отображается. MessageBox перед ним показывается и отрабатывает отлично, а именно на отображении диалога падает.
И ещё почему-то крэшится release-сборка запущенная не из под QtCreator при закрытии тестера, но только если вызывались функции из Qt-плагина. Если вызывались функции из плагина написанного на Delphi, тестер нормально закрывается.

p.s. Ну и вообще любые плевки в сторону дизайна кода, как что переделать стоит, были бы уместны
Записан
AlexWMF
Гость
« Ответ #14 : Март 03, 2012, 18:47 »

поправил где-то как-то, сравни файлы со своими и поймешь. Всё в аттаче.
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.063 секунд. Запросов: 21.