SetDllDirectory LoadLibrary внутри DLL

Могу ли я использовать C++ SetDllDirectory а также LoadLibrary команды внутри C++ DLL для загрузки другой DLL? Я пытался использовать их так:

Исполняемый файл вызывает 1-ую DLL, затем 1-я DLL загружает 2-ую DLL, затем 2-я DLL выполняет вычисления...

но когда я запускаю исполняемый файл, я получаю это сообщение об ошибке:

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

2-я DLL отлично работает, когда связана непосредственно с исполняемым файлом!

Это код внутри моего исполняемого файла:

#include <windows.h>
#include <iostream>

int main(){
    HINSTANCE hDLL_Link=NULL;
    SetDllDirectory((LPCWSTR)L"C:\\Users\\MC\\Documents\\2014_07_01a_FDD_VC2008\\test_call_DLL\\EXE_calls_Link_DLL\\Release");
    hDLL_Link=LoadLibrary((LPCWSTR)L"Link_DLL.dll");
    if(hDLL_Link==NULL) std::cout<<"did not load"<<'\n';
    typedef void (*Ptr_OPS_Link)();
    Ptr_OPS_Link Ptr_OPS_Link_0;
    Ptr_OPS_Link_0=(Ptr_OPS_Link)GetProcAddress(hDLL_Link,"OPS_Link");
    Ptr_OPS_Link_0();
    FreeLibrary(hDLL_Link);
    system("pause");
}

это код внутри первой DLL:

#include "Link.h"

extern "C" __declspec(dllexport)
void OPS_Link(){
    Link*Link_Ptr_Object=NULL;
    if(Link_Ptr_Object==NULL){
        Link_Ptr_Object=new Link();
    }
    if(Link_Ptr_Object==NULL){
        //can not throw inside __declspec(dllexport) functions marked extern "C" that's why std::cout is implemented:
        std::cout<<"Error: could not link to FDD DLL"<<'\n';
        system("pause");
    }
    delete Link_Ptr_Object;
    Link_Ptr_Object=NULL;
}

Link::Link()
:m_void_Ptr_ObjectByDLL(NULL){
    HINSTANCE hDLL=NULL;//handle to DLL
    SetDllDirectory((LPCWSTR)L"C:\\Software\\Octave-3.6.1\\bin\\");
    hDLL=LoadLibrary((LPCWSTR)L"C:\\Users\\MC\\Documents\\2014_07_01a_FDD_VC2008\\Executable\\Release\\FDD_DLL.dll");
    if(hDLL==NULL){
        throw "DLL loading could not be done";
    }else if(hDLL!=NULL){
        typedef void (*Ptr_OPS_FDD)(std::string, int, int);
        Ptr_OPS_FDD Ptr_OPS_FDD_0;//pointer to procedure inside DLL
        Ptr_OPS_FDD_0=NULL;
        Ptr_OPS_FDD_0=(Ptr_OPS_FDD)GetProcAddress(hDLL,"OPS_FDD");
        if(Ptr_OPS_FDD_0==NULL){
            FreeLibrary(hDLL);
            throw "DLL exported function address could not be determined";
        }else{
            //run the procedure inside DLL:
            Ptr_OPS_FDD_0("FDD_INPUT_Truss_Bridge_Data2_Ambient_Inch_11Channels_77824Samples_SamplingFreq_256Hz.txt",11,256);//LabScaleTruss
            //Ptr_OPS_FDD_0("FDD_INPUT_Ambient_EW_15Channels_3000Samples_SamplingFreq_20Hz.txt",15,20);//AmbientEW
            //Ptr_OPS_FDD_0("FDD_INPUT_Meriden_3Channels(3_5_8)_3686400Samples_SamplingFreq_2048Hz.txt",3,2048);//MeridenBridge
            FreeLibrary(hDLL);
        }
    }
}

3 ответа

Решение

В вашем коде есть несколько вещей, которые могут вызвать сбой:

  1. Вы не выходите, если DLL не может быть загружена:
  2. Вы передаете объекты, которые внутренне используют динамическое размещение и, таким образом, будут использовать диспетчер кучи.

За 1. выше, ваша функция main() выполняет только простую cout если библиотека не может быть найдена. Однако вместо выхода main Функция продолжается, как будто библиотека была найдена.

За 2. выше, проходя std::string в качестве параметра для DLL функция подвержена ошибкам и не рекомендуется, если вы точно не знаете , что делаете. Причина, по которой он подвержен ошибкам

  • Библиотека DLL, которая содержит вызов функции, может быть построена с другим набором параметров, чем библиотека DLL, которая вызывает функцию. Эти разные варианты могут вызвать разницу в том, как std::string реализовано, как выложено в память и т. д.

  • DLL, содержащая вызов функции, может быть создана другой версией компилятора, чем DLL, которая вызывает функцию. Опять та же проблема с различными реализациями std::string

  • DLL и модули, использующие std::string, возможно, не были построены с использованием DLL version библиотеки времени выполнения C. Если библиотеки DLL / модули не построены и не связаны с использованием версии DLL библиотеки времени выполнения, DLL будет использовать кучу, отличную от модуля. Любая операция с std::string будет недействительной из-за различий в куче памяти.

Итак, в двух словах, если вы не можете гарантировать, что

  1. Вы строите модули и библиотеки DLL с одинаковой версией параметров компилятора и компилятора.
  2. Вы связываете все модули с DLL-версией библиотеки времени выполнения.

Потом прохождение std::string в качестве параметра и, как правило, передача любого объекта, который поддерживает динамически выделенную память, может или приведет к ошибкам во время выполнения.

Помимо неадекватной обработки ошибок и использования стандартной библиотеки в разных модулях, необходимо учитывать еще два момента.

Могу ли я использовать SetDllDirectory в DLL для...?

Да, вы можете, но вы не должны! (Ошибки ждут, чтобы произойти).

Зачем? потому что единственным объектом, который отвечает за изменение среды, является основное приложение. Код библиотеки (статический или dll) не знает, в каком приложении он будет использоваться. Он может работать правильно в некоторых программах и может не работать в других.

Могу ли я использовать C++ LoadLibrary/FreeLibrary в DLL для...?

Да, вы можете, но не используйте их в функции dllmain, поскольку она может заблокировать вашу программу.

Я решил проблему, и показал, как здесь:

Я изменил код внутри исполняемого файла и 1-й DLL, как показано ниже, чтобы учесть обработку ошибок, а также добавил "return 0;"теперь исполняемый файл ссылается на 1-ую DLL, и она отлично работает... На самом деле проблема была в том, что main нужно return что-то... Я все "std::string" с "char*"На границах DLL... Кстати, причина, по которой я хочу разработать две библиотеки DLL, и я использую"SetDllDirectory"внутри 1-го я хочу вызвать DLL с C# GUI, и проблема в том, что нет"SetDllDirectory"команда доступна в C#, поэтому я пришел к идее разработки двух DLL, внутри первой DLL, которую я буду использовать"SetDllDirectory"чтобы позаботиться о необходимых зависимостях (DLL зависит от каталога Octave и Octave Bin), а затем я разработал 2-ую DLL, которая выполняет реальные вычисления... Я знаю, что есть некоторые методы, такие как"[DllImport("Kernel32.dll")]"и оттуда мы можем использовать"SetDllDirectory"в C#, но этот метод выглядит больно.

Исправленный код внутри исполняемого файла:

    #include <windows.h>
#include <iostream>

int main(){
    try{
        HINSTANCE hDLL_Link=NULL;
        hDLL_Link=LoadLibrary((LPCWSTR)L"Link_DLL.dll");
        if(hDLL_Link==NULL){
            throw "Link DLL did not load";
        }else{
            typedef void (*Ptr_OPS_Link)();
            Ptr_OPS_Link Ptr_OPS_Link_0=NULL;
            Ptr_OPS_Link_0=(Ptr_OPS_Link)GetProcAddress(hDLL_Link,"OPS_Link");
            if(Ptr_OPS_Link_0==NULL){
                throw "Link DLL exported function not found";
                FreeLibrary(hDLL_Link);
            }else{
                Ptr_OPS_Link_0();
                FreeLibrary(hDLL_Link);
            }
        }
    }
    catch(char*char_Ptr_Exception){
        std::cerr<<"Error: "<<char_Ptr_Exception<<'\n';
    }
    system("pause");
    return 0;
}

Исправленный код внутри 1-й DLL:

    #include "Link.h"

extern "C" __declspec(dllexport)
void OPS_Link(){
    Link*Link_Ptr_Object=NULL;
    if(Link_Ptr_Object==NULL){
        Link_Ptr_Object=new Link();
    }
    if(Link_Ptr_Object==NULL){
        ////can not throw inside __declspec(dllexport) functions marked extern "C" that's why std::cout is implemented:
        //std::cout<<"Error: could not link to FDD DLL"<<'\n';
        system("pause");
    }
    delete Link_Ptr_Object;
    Link_Ptr_Object=NULL;
}

Link::Link()
:m_void_Ptr_ObjectByDLL(NULL){
    HINSTANCE hDLL=NULL;//handle to DLL
    SetDllDirectory((LPCWSTR)L"C:\\Software\\Octave-3.6.1\\bin\\");
    //path relative to executable (C# executable or C++ executable)
    hDLL=LoadLibrary((LPCWSTR)L"FDD_DLL.dll");
    if(hDLL==NULL){
        throw "FDD DLL did not load";
    }else if(hDLL!=NULL){
        typedef void (*Ptr_OPS_FDD)(char*, int, int);
        Ptr_OPS_FDD Ptr_OPS_FDD_0;//pointer to procedure inside DLL
        Ptr_OPS_FDD_0=NULL;
        Ptr_OPS_FDD_0=(Ptr_OPS_FDD)GetProcAddress(hDLL,"OPS_FDD");
        if(Ptr_OPS_FDD_0==NULL){
            throw "FDD DLL exported function not found";
            FreeLibrary(hDLL);
        }else{
            //run the procedure inside DLL:
            Ptr_OPS_FDD_0("FDD_INPUT_Truss_Bridge_Data2_Ambient_Inch_11Channels_77824Samples_SamplingFreq_256Hz.txt",11,256);//LabScaleTruss
            //Ptr_OPS_FDD_0("FDD_INPUT_Ambient_EW_15Channels_3000Samples_SamplingFreq_20Hz.txt",15,20);//AmbientEW
            //Ptr_OPS_FDD_0("FDD_INPUT_Meriden_3Channels(3_5_8)_3686400Samples_SamplingFreq_2048Hz.txt",3,2048);//MeridenBridge
            FreeLibrary(hDLL);
        }
    }
}
Другие вопросы по тегам